Merge "Clean up trivial package refactor TODOs"
diff --git a/Android.bp b/Android.bp
index e756b34..eacf57c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -366,7 +366,8 @@
         ":framework-tethering-srcs",
         ":framework-wifi-updatable-sources",
         ":updatable-media-srcs",
-    ]
+    ],
+    visibility: ["//visibility:private"],
 }
 
 java_library {
@@ -383,7 +384,31 @@
         "framework-wifi.stubs.module_lib",
     ],
     sdk_version: "module_current",
-    visibility: [":__pkg__"],
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "framework-all",
+    installable: false,
+    static_libs: [
+        "framework-minus-apex",
+        "framework-appsearch",
+        "framework-graphics.impl",
+        "framework-mediaprovider.impl",
+        "framework-permission.impl",
+        "framework-sdkextensions.impl",
+        "framework-statsd.impl",
+        "framework-tethering.impl",
+        "framework-wifi.impl",
+        "updatable-media",
+    ],
+    apex_available: ["//apex_available:platform"],
+    sdk_version: "core_platform",
+    visibility: [
+        // DO NOT ADD ANY MORE ENTRIES TO THIS LIST
+        "//external/robolectric-shadows:__subpackages__",
+        "//frameworks/layoutlib:__subpackages__",
+    ],
 }
 
 filegroup {
@@ -474,53 +499,6 @@
     installable: false,
 }
 
-java_defaults {
-    name: "framework-defaults",
-    defaults: ["framework-aidl-export-defaults"],
-    installable: true,
-
-    aidl: {
-        generate_get_transaction_name: true,
-    },
-
-    srcs: ["core/java/**/*.logtags"],
-
-    exclude_srcs: [
-        // See comment on framework-atb-backward-compatibility module below
-        "core/java/android/content/pm/AndroidTestBaseUpdater.java",
-    ],
-
-    sdk_version: "core_platform",
-    libs: [
-        "app-compat-annotations",
-        "ext",
-        "unsupportedappusage",
-    ],
-
-    jarjar_rules: ":framework-jarjar-rules",
-
-    static_libs: [
-        "framework-internal-utils",
-    ],
-
-    dxflags: [
-        "--core-library",
-        "--multi-dex",
-    ],
-
-    plugins: [
-        "view-inspector-annotation-processor",
-        "staledataclass-annotation-processor",
-        "error_prone_android_framework",
-    ],
-
-    required: [
-        // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
-        "gps_debug.conf",
-        "protolog.conf.json.gz",
-    ],
-}
-
 filegroup {
     name: "framework-jarjar-rules",
     srcs: ["framework-jarjar-rules.txt"],
@@ -560,19 +538,47 @@
 
 java_library {
     name: "framework-minus-apex",
-    defaults: ["framework-defaults"],
-    srcs: [":framework-non-updatable-sources"],
+    defaults: ["framework-aidl-export-defaults"],
+    srcs: [
+        ":framework-non-updatable-sources",
+        "core/java/**/*.logtags",
+    ],
+    // See comment on framework-atb-backward-compatibility module below
+    exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"],
+    aidl: {
+        generate_get_transaction_name: true,
+    },
+    dxflags: [
+        "--core-library",
+        "--multi-dex",
+    ],
     installable: true,
+    jarjar_rules: ":framework-jarjar-rules",
     javac_shard_size: 150,
+    plugins: [
+        "view-inspector-annotation-processor",
+        "staledataclass-annotation-processor",
+        "error_prone_android_framework",
+    ],
     required: [
         "framework-platform-compat-config",
+        // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
+        "gps_debug.conf",
         "libcore-platform-compat-config",
+        "protolog.conf.json.gz",
         "services-platform-compat-config",
         "documents-ui-compat-config",
         "calendar-provider-compat-config",
     ],
-    libs: ["framework-updatable-stubs-module_libs_api"],
+    libs: [
+        "app-compat-annotations",
+        "ext",
+        "framework-updatable-stubs-module_libs_api",
+        "unsupportedappusage",
+    ],
+    sdk_version: "core_platform",
     static_libs: [
+        "framework-internal-utils",
         // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
         // in favor of an API stubs dependency in java_library "framework" below.
         "mimemap",
@@ -615,32 +621,6 @@
     apex_available: ["//apex_available:platform"],
 }
 
-java_library {
-    name: "framework-all",
-    defaults: ["framework-defaults"],
-    srcs: [":framework-all-sources"],
-    installable: false,
-    static_libs: [
-        "exoplayer2-extractor",
-        "android.hardware.wifi-V1.0-java-constants",
-        "mediatranscoding_aidl_interface-java",
-
-        // Additional dependencies needed to build the ike API classes.
-        "ike-internals",
-    ],
-    plugins: [
-        "intdef-annotation-processor",
-    ],
-    libs: ["icing-java-proto-lite"],
-    apex_available: ["//apex_available:platform"],
-    visibility: [
-        // DO NOT ADD ANY MORE ENTRIES TO THIS LIST
-        "//external/robolectric-shadows:__subpackages__",
-        "//frameworks/base",
-        "//frameworks/layoutlib:__subpackages__",
-    ],
-}
-
 platform_compat_config {
    name: "framework-platform-compat-config",
    src: ":framework-minus-apex",
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
index b1dbb28..d07ed37 100644
--- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -51,13 +51,14 @@
         sServiceWatcher = null;
     }
 
-    public static void clearServiceWatcher() {
-        if (sServiceWatcher != null) {
-            if (sServiceWatcher.mReadyToClear) {
-                sServiceWatcher.mService = null;
+    private static void clearServiceWatcher() {
+        final ServiceWatcher sw = sServiceWatcher;
+        if (sw != null) {
+            if (sw.mReadyToClear) {
+                sw.mService = null;
                 sServiceWatcher = null;
             } else {
-                sServiceWatcher.mReadyToClear = true;
+                sw.mReadyToClear = true;
             }
         }
     }
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index 050fecd..d3938f4 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -17,7 +17,6 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.view.Display;
@@ -136,4 +135,22 @@
             }
         }
     }
+
+    @Test
+    public void getDisplayMetrics() {
+        ResourcesManager resourcesManager = ResourcesManager.getInstance();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            // Invalidate cache.
+            resourcesManager.applyConfigurationToResourcesLocked(
+                    resourcesManager.getConfiguration(), null);
+            state.resumeTiming();
+
+            // Invoke twice for testing cache.
+            resourcesManager.getDisplayMetrics();
+            resourcesManager.getDisplayMetrics();
+        }
+    }
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index 410e211..266e672 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -101,20 +101,16 @@
 
     annotations_enabled: true,
 
-    stubs_library_visibility: [
-        "//visibility:public",
-    ],
+    // Allow access to the stubs from anywhere
+    visibility: ["//visibility:public"],
+    stubs_library_visibility: ["//visibility:public"],
 
-    // Set the visibility of the modules creating the stubs source.
-    stubs_source_visibility: [
-        // Ignore any visibility rules specified on the java_sdk_library when
-        // setting the visibility of the stubs source modules.
-        "//visibility:override",
-
-        // Currently, the stub source is not required for anything other than building
-        // the stubs library so is private to avoid misuse.
-        "//visibility:private",
+    // Hide impl library and stub sources
+    impl_library_visibility: [
+        ":__package__",
+        "//frameworks/base", // For framework-all
     ],
+    stubs_source_visibility: ["//visibility:private"],
 
     // Collates API usages from each module for further analysis.
     plugins: ["java_api_finder"],
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 6c3398f..d8386b5 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -21,14 +21,12 @@
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -63,11 +61,10 @@
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
-import android.util.KeyValueListParser;
 import android.util.MutableLong;
 import android.util.Pair;
 import android.util.Slog;
@@ -856,7 +853,7 @@
      * global Settings. Any access to this class or its fields should be done while
      * holding the DeviceIdleController lock.
      */
-    public final class Constants extends ContentObserver {
+    public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
         // Key names stored in the settings value.
         private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
                 = "light_after_inactive_to";
@@ -884,15 +881,15 @@
         private static final String KEY_MAX_IDLE_TIMEOUT = "max_idle_to";
         private static final String KEY_IDLE_FACTOR = "idle_factor";
         private static final String KEY_MIN_TIME_TO_ALARM = "min_time_to_alarm";
+        // TODO(166121524): update flag names
         private static final String KEY_MAX_TEMP_APP_WHITELIST_DURATION =
                 "max_temp_app_whitelist_duration";
         private static final String KEY_MMS_TEMP_APP_WHITELIST_DURATION =
                 "mms_temp_app_whitelist_duration";
         private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION =
                 "sms_temp_app_whitelist_duration";
-        // TODO(b/124466289): update value to match the name
-        private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION =
-                "notification_whitelist_duration";
+        private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION_MS =
+                "notification_allowlist_duration_ms";
         /**
          * Whether to wait for the user to unlock the device before causing screen-on to
          * exit doze. Default = true
@@ -903,52 +900,106 @@
         private static final String KEY_PRE_IDLE_FACTOR_SHORT =
                 "pre_idle_factor_short";
 
+        private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
+                !COMPRESS_TIME ? 3 * 60 * 1000L : 15 * 1000L;
+        private static final long DEFAULT_LIGHT_PRE_IDLE_TIMEOUT =
+                !COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L;
+        private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
+                !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
+        private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
+        private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
+                !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
+        private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
+                !COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L;
+        private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET =
+                !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
+        private static final long DEFAULT_MIN_LIGHT_MAINTENANCE_TIME =
+                !COMPRESS_TIME ? 5 * 1000L : 1 * 1000L;
+        private static final long DEFAULT_MIN_DEEP_MAINTENANCE_TIME =
+                !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L;
+        private static final long DEFAULT_INACTIVE_TIMEOUT =
+                (30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
+        private static final long DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY =
+                (15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
+        private static final long DEFAULT_SENSING_TIMEOUT =
+                !COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L;
+        private static final long DEFAULT_LOCATING_TIMEOUT =
+                !COMPRESS_TIME ? 30 * 1000L : 15 * 1000L;
+        private static final float DEFAULT_LOCATION_ACCURACY = 20f;
+        private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT =
+                !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
+        private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT =
+                (30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
+        private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY =
+                (15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
+        private static final long DEFAULT_IDLE_PENDING_TIMEOUT =
+                !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
+        private static final long DEFAULT_MAX_IDLE_PENDING_TIMEOUT =
+                !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
+        private static final float DEFAULT_IDLE_PENDING_FACTOR = 2f;
+        private static final long DEFAULT_QUICK_DOZE_DELAY_TIMEOUT =
+                !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
+        private static final long DEFAULT_IDLE_TIMEOUT =
+                !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L;
+        private static final long DEFAULT_MAX_IDLE_TIMEOUT =
+                !COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L;
+        private static final float DEFAULT_IDLE_FACTOR = 2f;
+        private static final long DEFAULT_MIN_TIME_TO_ALARM =
+                !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L;
+        private static final long DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION = 5 * 60 * 1000L;
+        private static final long DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION = 60 * 1000L;
+        private static final long DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION = 20 * 1000L;
+        private static final long DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS = 30 * 1000L;
+        private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
+        private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
+        private static final float DEFAULT_PRE_IDLE_FACTOR_SHORT = .33f;
+
         /**
          * This is the time, after becoming inactive, that we go in to the first
          * light-weight idle mode.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
          */
-        public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+        public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
 
         /**
          * This is amount of time we will wait from the point where we decide we would
          * like to go idle until we actually do, while waiting for jobs and other current
          * activity to finish.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_LIGHT_PRE_IDLE_TIMEOUT
          */
-        public long LIGHT_PRE_IDLE_TIMEOUT;
+        public long LIGHT_PRE_IDLE_TIMEOUT = DEFAULT_LIGHT_PRE_IDLE_TIMEOUT;
 
         /**
          * This is the initial time that we will run in idle maintenance mode.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_LIGHT_IDLE_TIMEOUT
          */
-        public long LIGHT_IDLE_TIMEOUT;
+        public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
 
         /**
          * Scaling factor to apply to the light idle mode time each time we complete a cycle.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_LIGHT_IDLE_FACTOR
          */
-        public float LIGHT_IDLE_FACTOR;
+        public float LIGHT_IDLE_FACTOR = DEFAULT_LIGHT_IDLE_FACTOR;
 
         /**
          * This is the maximum time we will run in idle maintenance mode.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
          */
-        public long LIGHT_MAX_IDLE_TIMEOUT;
+        public long LIGHT_MAX_IDLE_TIMEOUT = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT;
 
         /**
          * This is the minimum amount of time we want to make available for maintenance mode
          * when lightly idling.  That is, we will always have at least this amount of time
          * available maintenance before timing out and cutting off maintenance mode.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
          */
-        public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+        public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
 
         /**
          * This is the maximum amount of time we want to make available for maintenance mode
@@ -956,10 +1007,10 @@
          * budget and this time is being added to the budget reserve, this is the maximum
          * reserve size we will allow to grow and thus the maximum amount of time we will
          * allow for the maintenance window.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
          */
-        public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+        public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
 
         /**
          * This is the minimum amount of time that we will stay in maintenance mode after
@@ -967,10 +1018,10 @@
          * in to maintenance mode and scheduling their work -- otherwise we may
          * see there is nothing to do (no jobs pending) and go out of maintenance
          * mode immediately.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_MIN_LIGHT_MAINTENANCE_TIME
          */
-        public long MIN_LIGHT_MAINTENANCE_TIME;
+        public long MIN_LIGHT_MAINTENANCE_TIME = DEFAULT_MIN_LIGHT_MAINTENANCE_TIME;
 
         /**
          * This is the minimum amount of time that we will stay in maintenance mode after
@@ -978,271 +1029,323 @@
          * in to maintenance mode and scheduling their work -- otherwise we may
          * see there is nothing to do (no jobs pending) and go out of maintenance
          * mode immediately.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MIN_DEEP_MAINTENANCE_TIME
          */
-        public long MIN_DEEP_MAINTENANCE_TIME;
+        public long MIN_DEEP_MAINTENANCE_TIME = DEFAULT_MIN_DEEP_MAINTENANCE_TIME;
 
         /**
          * This is the time, after becoming inactive, at which we start looking at the
          * motion sensor to determine if the device is being left alone.  We don't do this
          * immediately after going inactive just because we don't want to be continually running
          * the motion sensor whenever the screen is off.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_INACTIVE_TIMEOUT
          */
-        public long INACTIVE_TIMEOUT;
+        public long INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT;
 
         /**
          * If we don't receive a callback from AnyMotion in this amount of time +
          * {@link #LOCATING_TIMEOUT}, we will change from
          * STATE_SENSING to STATE_INACTIVE, and any AnyMotion callbacks while not in STATE_SENSING
          * will be ignored.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_SENSING_TIMEOUT
          */
-        public long SENSING_TIMEOUT;
+        public long SENSING_TIMEOUT = DEFAULT_SENSING_TIMEOUT;
 
         /**
          * This is how long we will wait to try to get a good location fix before going in to
          * idle mode.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_LOCATING_TIMEOUT
          */
-        public long LOCATING_TIMEOUT;
+        public long LOCATING_TIMEOUT = DEFAULT_LOCATING_TIMEOUT;
 
         /**
          * The desired maximum accuracy (in meters) we consider the location to be good enough to go
          * on to idle.  We will be trying to get an accuracy fix at least this good or until
          * {@link #LOCATING_TIMEOUT} expires.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_LOCATION_ACCURACY
          */
-        public float LOCATION_ACCURACY;
+        public float LOCATION_ACCURACY = DEFAULT_LOCATION_ACCURACY;
 
         /**
          * This is the time, after seeing motion, that we wait after becoming inactive from
          * that until we start looking for motion again.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MOTION_INACTIVE_TIMEOUT
          */
-        public long MOTION_INACTIVE_TIMEOUT;
+        public long MOTION_INACTIVE_TIMEOUT = DEFAULT_MOTION_INACTIVE_TIMEOUT;
 
         /**
          * This is the time, after the inactive timeout elapses, that we will wait looking
          * for motion until we truly consider the device to be idle.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
          */
-        public long IDLE_AFTER_INACTIVE_TIMEOUT;
+        public long IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
 
         /**
          * This is the initial time, after being idle, that we will allow ourself to be back
          * in the IDLE_MAINTENANCE state allowing the system to run normally until we return to
          * idle.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_IDLE_PENDING_TIMEOUT
          */
-        public long IDLE_PENDING_TIMEOUT;
+        public long IDLE_PENDING_TIMEOUT = DEFAULT_IDLE_PENDING_TIMEOUT;
 
         /**
          * Maximum pending idle timeout (time spent running) we will be allowed to use.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MAX_IDLE_PENDING_TIMEOUT
          */
-        public long MAX_IDLE_PENDING_TIMEOUT;
+        public long MAX_IDLE_PENDING_TIMEOUT = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
 
         /**
          * Scaling factor to apply to current pending idle timeout each time we cycle through
          * that state.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_IDLE_PENDING_FACTOR
          */
-        public float IDLE_PENDING_FACTOR;
+        public float IDLE_PENDING_FACTOR = DEFAULT_IDLE_PENDING_FACTOR;
 
         /**
          * This is amount of time we will wait from the point where we go into
          * STATE_QUICK_DOZE_DELAY until we actually go into STATE_IDLE, while waiting for jobs
          * and other current activity to finish.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_QUICK_DOZE_DELAY_TIMEOUT
          */
-        public long QUICK_DOZE_DELAY_TIMEOUT;
+        public long QUICK_DOZE_DELAY_TIMEOUT = DEFAULT_QUICK_DOZE_DELAY_TIMEOUT;
 
         /**
          * This is the initial time that we want to sit in the idle state before waking up
          * again to return to pending idle and allowing normal work to run.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_IDLE_TIMEOUT
          */
-        public long IDLE_TIMEOUT;
+        public long IDLE_TIMEOUT = DEFAULT_IDLE_TIMEOUT;
 
         /**
          * Maximum idle duration we will be allowed to use.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MAX_IDLE_TIMEOUT
          */
-        public long MAX_IDLE_TIMEOUT;
+        public long MAX_IDLE_TIMEOUT = DEFAULT_MAX_IDLE_TIMEOUT;
 
         /**
          * Scaling factor to apply to current idle timeout each time we cycle through that state.
-          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_IDLE_FACTOR
          */
-        public float IDLE_FACTOR;
+        public float IDLE_FACTOR = DEFAULT_IDLE_FACTOR;
 
         /**
          * This is the minimum time we will allow until the next upcoming alarm for us to
          * actually go in to idle mode.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MIN_TIME_TO_ALARM
          */
-        public long MIN_TIME_TO_ALARM;
+        public long MIN_TIME_TO_ALARM = DEFAULT_MIN_TIME_TO_ALARM;
 
         /**
          * Max amount of time to temporarily whitelist an app when it receives a high priority
          * tickle.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         *
          * @see #KEY_MAX_TEMP_APP_WHITELIST_DURATION
          */
-        public long MAX_TEMP_APP_WHITELIST_DURATION;
+        public long MAX_TEMP_APP_WHITELIST_DURATION = DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION;
 
         /**
          * Amount of time we would like to whitelist an app that is receiving an MMS.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_MMS_TEMP_APP_WHITELIST_DURATION
          */
-        public long MMS_TEMP_APP_WHITELIST_DURATION;
+        public long MMS_TEMP_APP_WHITELIST_DURATION = DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION;
 
         /**
          * Amount of time we would like to whitelist an app that is receiving an SMS.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_SMS_TEMP_APP_WHITELIST_DURATION
          */
-        public long SMS_TEMP_APP_WHITELIST_DURATION;
+        public long SMS_TEMP_APP_WHITELIST_DURATION = DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION;
 
         /**
          * Amount of time we would like to whitelist an app that is handling a
          * {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
-         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
-         * @see #KEY_NOTIFICATION_ALLOWLIST_DURATION
+         * @see #KEY_NOTIFICATION_ALLOWLIST_DURATION_MS
          */
-        public long NOTIFICATION_ALLOWLIST_DURATION;
+        public long NOTIFICATION_ALLOWLIST_DURATION_MS = DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS;
 
         /**
          * Pre idle time factor use to make idle delay longer
          */
-        public float PRE_IDLE_FACTOR_LONG;
+        public float PRE_IDLE_FACTOR_LONG = DEFAULT_PRE_IDLE_FACTOR_LONG;
 
         /**
          * Pre idle time factor use to make idle delay shorter
          */
-        public float PRE_IDLE_FACTOR_SHORT;
+        public float PRE_IDLE_FACTOR_SHORT = DEFAULT_PRE_IDLE_FACTOR_SHORT;
 
-        public boolean WAIT_FOR_UNLOCK;
+        public boolean WAIT_FOR_UNLOCK = DEFAULT_WAIT_FOR_UNLOCK;
 
-        private final ContentResolver mResolver;
         private final boolean mSmallBatteryDevice;
-        private final KeyValueListParser mParser = new KeyValueListParser(',');
 
-        public Constants(Handler handler, ContentResolver resolver) {
-            super(handler);
-            mResolver = resolver;
+        public Constants() {
             mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
-            mResolver.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
-                    false, this);
-            updateConstants();
+            if (mSmallBatteryDevice) {
+                INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY;
+                IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY;
+            }
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DEVICE_IDLE,
+                    JobSchedulerBackgroundThread.getExecutor(), this);
+            // Load all the constants.
+            onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
         }
 
+
         @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            updateConstants();
-        }
-
-        private void updateConstants() {
+        public void onPropertiesChanged(DeviceConfig.Properties properties) {
             synchronized (DeviceIdleController.this) {
-                try {
-                    mParser.setString(Settings.Global.getString(mResolver,
-                            Settings.Global.DEVICE_IDLE_CONSTANTS));
-                } catch (IllegalArgumentException e) {
-                    // Failed to parse the settings string, log this and move on
-                    // with defaults.
-                    Slog.e(TAG, "Bad device idle settings", e);
+                for (String name : properties.getKeyset()) {
+                    if (name == null) {
+                        continue;
+                    }
+                    switch (name) {
+                        case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
+                            LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
+                                    KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+                                    DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+                            break;
+                        case KEY_LIGHT_PRE_IDLE_TIMEOUT:
+                            LIGHT_PRE_IDLE_TIMEOUT = properties.getLong(
+                                    KEY_LIGHT_PRE_IDLE_TIMEOUT, DEFAULT_LIGHT_PRE_IDLE_TIMEOUT);
+                            break;
+                        case KEY_LIGHT_IDLE_TIMEOUT:
+                            LIGHT_IDLE_TIMEOUT = properties.getLong(
+                                    KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
+                            break;
+                        case KEY_LIGHT_IDLE_FACTOR:
+                            LIGHT_IDLE_FACTOR = properties.getFloat(
+                                    KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR);
+                            break;
+                        case KEY_LIGHT_MAX_IDLE_TIMEOUT:
+                            LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
+                                    KEY_LIGHT_MAX_IDLE_TIMEOUT, DEFAULT_LIGHT_MAX_IDLE_TIMEOUT);
+                            break;
+                        case KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET:
+                            LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = properties.getLong(
+                                    KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
+                                    DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
+                            break;
+                        case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
+                            LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
+                                    KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
+                                    DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET);
+                            break;
+                        case KEY_MIN_LIGHT_MAINTENANCE_TIME:
+                            MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
+                                    KEY_MIN_LIGHT_MAINTENANCE_TIME,
+                                    DEFAULT_MIN_LIGHT_MAINTENANCE_TIME);
+                            break;
+                        case KEY_MIN_DEEP_MAINTENANCE_TIME:
+                            MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
+                                    KEY_MIN_DEEP_MAINTENANCE_TIME,
+                                    DEFAULT_MIN_DEEP_MAINTENANCE_TIME);
+                            break;
+                        case KEY_INACTIVE_TIMEOUT:
+                            final long defaultInactiveTimeout = mSmallBatteryDevice
+                                    ? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
+                                    : DEFAULT_INACTIVE_TIMEOUT;
+                            INACTIVE_TIMEOUT = properties.getLong(
+                                    KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
+                            break;
+                        case KEY_SENSING_TIMEOUT:
+                            SENSING_TIMEOUT = properties.getLong(
+                                    KEY_SENSING_TIMEOUT, DEFAULT_SENSING_TIMEOUT);
+                            break;
+                        case KEY_LOCATING_TIMEOUT:
+                            LOCATING_TIMEOUT = properties.getLong(
+                                    KEY_LOCATING_TIMEOUT, DEFAULT_LOCATING_TIMEOUT);
+                            break;
+                        case KEY_LOCATION_ACCURACY:
+                            LOCATION_ACCURACY = properties.getFloat(
+                                    KEY_LOCATION_ACCURACY, DEFAULT_LOCATION_ACCURACY);
+                            break;
+                        case KEY_MOTION_INACTIVE_TIMEOUT:
+                            MOTION_INACTIVE_TIMEOUT = properties.getLong(
+                                    KEY_MOTION_INACTIVE_TIMEOUT, DEFAULT_MOTION_INACTIVE_TIMEOUT);
+                            break;
+                        case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
+                            final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
+                                    ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
+                                    : DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
+                            IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
+                                    KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
+                                    defaultIdleAfterInactiveTimeout);
+                            break;
+                        case KEY_IDLE_PENDING_TIMEOUT:
+                            IDLE_PENDING_TIMEOUT = properties.getLong(
+                                    KEY_IDLE_PENDING_TIMEOUT, DEFAULT_IDLE_PENDING_TIMEOUT);
+                            break;
+                        case KEY_MAX_IDLE_PENDING_TIMEOUT:
+                            MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
+                                    KEY_MAX_IDLE_PENDING_TIMEOUT, DEFAULT_MAX_IDLE_PENDING_TIMEOUT);
+                            break;
+                        case KEY_IDLE_PENDING_FACTOR:
+                            IDLE_PENDING_FACTOR = properties.getFloat(
+                                    KEY_IDLE_PENDING_FACTOR, DEFAULT_IDLE_PENDING_FACTOR);
+                            break;
+                        case KEY_QUICK_DOZE_DELAY_TIMEOUT:
+                            QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
+                                    KEY_QUICK_DOZE_DELAY_TIMEOUT, DEFAULT_QUICK_DOZE_DELAY_TIMEOUT);
+                            break;
+                        case KEY_IDLE_TIMEOUT:
+                            IDLE_TIMEOUT = properties.getLong(
+                                    KEY_IDLE_TIMEOUT, DEFAULT_IDLE_TIMEOUT);
+                            break;
+                        case KEY_MAX_IDLE_TIMEOUT:
+                            MAX_IDLE_TIMEOUT = properties.getLong(
+                                    KEY_MAX_IDLE_TIMEOUT, DEFAULT_MAX_IDLE_TIMEOUT);
+                            break;
+                        case KEY_IDLE_FACTOR:
+                            IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, DEFAULT_IDLE_FACTOR);
+                            break;
+                        case KEY_MIN_TIME_TO_ALARM:
+                            MIN_TIME_TO_ALARM = properties.getLong(
+                                    KEY_MIN_TIME_TO_ALARM, DEFAULT_MIN_TIME_TO_ALARM);
+                            break;
+                        case KEY_MAX_TEMP_APP_WHITELIST_DURATION:
+                            MAX_TEMP_APP_WHITELIST_DURATION = properties.getLong(
+                                    KEY_MAX_TEMP_APP_WHITELIST_DURATION,
+                                    DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION);
+                            break;
+                        case KEY_MMS_TEMP_APP_WHITELIST_DURATION:
+                            MMS_TEMP_APP_WHITELIST_DURATION = properties.getLong(
+                                    KEY_MMS_TEMP_APP_WHITELIST_DURATION,
+                                    DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION);
+                            break;
+                        case KEY_SMS_TEMP_APP_WHITELIST_DURATION:
+                            SMS_TEMP_APP_WHITELIST_DURATION = properties.getLong(
+                                    KEY_SMS_TEMP_APP_WHITELIST_DURATION,
+                                    DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION);
+                            break;
+                        case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
+                            NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
+                                    KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
+                                    DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS);
+                            break;
+                        case KEY_WAIT_FOR_UNLOCK:
+                            WAIT_FOR_UNLOCK = properties.getBoolean(
+                                    KEY_WAIT_FOR_UNLOCK, DEFAULT_WAIT_FOR_UNLOCK);
+                            break;
+                        case KEY_PRE_IDLE_FACTOR_LONG:
+                            PRE_IDLE_FACTOR_LONG = properties.getFloat(
+                                    KEY_PRE_IDLE_FACTOR_LONG, DEFAULT_PRE_IDLE_FACTOR_LONG);
+                            break;
+                        case KEY_PRE_IDLE_FACTOR_SHORT:
+                            PRE_IDLE_FACTOR_SHORT = properties.getFloat(
+                                    KEY_PRE_IDLE_FACTOR_SHORT, DEFAULT_PRE_IDLE_FACTOR_SHORT);
+                            break;
+                        default:
+                            Slog.e(TAG, "Unknown configuration key: " + name);
+                            break;
+                    }
                 }
-
-                LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getDurationMillis(
-                        KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
-                        !COMPRESS_TIME ? 3 * 60 * 1000L : 15 * 1000L);
-                LIGHT_PRE_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_PRE_IDLE_TIMEOUT,
-                        !COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L);
-                LIGHT_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_IDLE_TIMEOUT,
-                        !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);
-                LIGHT_IDLE_FACTOR = mParser.getFloat(KEY_LIGHT_IDLE_FACTOR,
-                        2f);
-                LIGHT_MAX_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_MAX_IDLE_TIMEOUT,
-                        !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L);
-                LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getDurationMillis(
-                        KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
-                        !COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L);
-                LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mParser.getDurationMillis(
-                        KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
-                        !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
-                MIN_LIGHT_MAINTENANCE_TIME = mParser.getDurationMillis(
-                        KEY_MIN_LIGHT_MAINTENANCE_TIME,
-                        !COMPRESS_TIME ? 5 * 1000L : 1 * 1000L);
-                MIN_DEEP_MAINTENANCE_TIME = mParser.getDurationMillis(
-                        KEY_MIN_DEEP_MAINTENANCE_TIME,
-                        !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
-                long inactiveTimeoutDefault = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
-                INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_INACTIVE_TIMEOUT,
-                        !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
-                SENSING_TIMEOUT = mParser.getDurationMillis(KEY_SENSING_TIMEOUT,
-                        !COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L);
-                LOCATING_TIMEOUT = mParser.getDurationMillis(KEY_LOCATING_TIMEOUT,
-                        !COMPRESS_TIME ? 30 * 1000L : 15 * 1000L);
-                LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
-                MOTION_INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_MOTION_INACTIVE_TIMEOUT,
-                        !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
-                long idleAfterInactiveTimeout = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
-                IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getDurationMillis(
-                        KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
-                        !COMPRESS_TIME ? idleAfterInactiveTimeout
-                                       : (idleAfterInactiveTimeout / 10));
-                IDLE_PENDING_TIMEOUT = mParser.getDurationMillis(KEY_IDLE_PENDING_TIMEOUT,
-                        !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
-                MAX_IDLE_PENDING_TIMEOUT = mParser.getDurationMillis(KEY_MAX_IDLE_PENDING_TIMEOUT,
-                        !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
-                IDLE_PENDING_FACTOR = mParser.getFloat(KEY_IDLE_PENDING_FACTOR,
-                        2f);
-                QUICK_DOZE_DELAY_TIMEOUT = mParser.getDurationMillis(
-                        KEY_QUICK_DOZE_DELAY_TIMEOUT, !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L);
-                IDLE_TIMEOUT = mParser.getDurationMillis(KEY_IDLE_TIMEOUT,
-                        !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);
-                MAX_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_MAX_IDLE_TIMEOUT,
-                        !COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L);
-                IDLE_FACTOR = mParser.getFloat(KEY_IDLE_FACTOR,
-                        2f);
-                MIN_TIME_TO_ALARM = mParser.getDurationMillis(KEY_MIN_TIME_TO_ALARM,
-                        !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L);
-                MAX_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
-                        KEY_MAX_TEMP_APP_WHITELIST_DURATION, 5 * 60 * 1000L);
-                MMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
-                        KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);
-                SMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
-                        KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
-                NOTIFICATION_ALLOWLIST_DURATION = mParser.getDurationMillis(
-                        KEY_NOTIFICATION_ALLOWLIST_DURATION, 30 * 1000L);
-                WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, true);
-                PRE_IDLE_FACTOR_LONG = mParser.getFloat(KEY_PRE_IDLE_FACTOR_LONG, 1.67f);
-                PRE_IDLE_FACTOR_SHORT = mParser.getFloat(KEY_PRE_IDLE_FACTOR_SHORT, 0.33f);
             }
         }
 
         void dump(PrintWriter pw) {
             pw.println("  Settings:");
 
-            pw.print("    "); pw.print(KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT); pw.print("=");
+            pw.print("    ");
+            pw.print(KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+            pw.print("=");
             TimeUtils.formatDuration(LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT, pw);
             pw.println();
 
@@ -1344,8 +1447,8 @@
             TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw);
             pw.println();
 
-            pw.print("    "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION); pw.print("=");
-            TimeUtils.formatDuration(NOTIFICATION_ALLOWLIST_DURATION, pw);
+            pw.print("    "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION_MS); pw.print("=");
+            TimeUtils.formatDuration(NOTIFICATION_ALLOWLIST_DURATION_MS, pw);
             pw.println();
 
             pw.print("    "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
@@ -1794,7 +1897,7 @@
         // duration in milliseconds
         @Override
         public long getNotificationAllowlistDuration() {
-            return mConstants.NOTIFICATION_ALLOWLIST_DURATION;
+            return mConstants.NOTIFICATION_ALLOWLIST_DURATION_MS;
         }
 
         @Override
@@ -1871,10 +1974,9 @@
             return mConnectivityManager;
         }
 
-        Constants getConstants(DeviceIdleController controller, Handler handler,
-                ContentResolver resolver) {
+        Constants getConstants(DeviceIdleController controller) {
             if (mConstants == null) {
-                mConstants = controller.new Constants(handler, resolver);
+                mConstants = controller.new Constants();
             }
             return mConstants;
         }
@@ -2023,7 +2125,7 @@
                 }
             }
 
-            mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver());
+            mConstants = mInjector.getConstants(this);
 
             readConfigFileLocked();
             updateWhitelistAppIdsLocked();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
new file mode 100644
index 0000000..f672e4b
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2020 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.alarm;
+
+import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.AlarmManager.RTC;
+import static android.app.AlarmManager.RTC_WAKEUP;
+
+import android.app.AlarmManager;
+import android.app.IAlarmListener;
+import android.app.PendingIntent;
+import android.os.WorkSource;
+import android.util.IndentingPrintWriter;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+class Alarm {
+    public final int type;
+    public final long origWhen;
+    public final boolean wakeup;
+    public final PendingIntent operation;
+    public final IAlarmListener listener;
+    public final String listenerTag;
+    public final String statsTag;
+    public final WorkSource workSource;
+    public final int flags;
+    public final AlarmManager.AlarmClockInfo alarmClock;
+    public final int uid;
+    public final int creatorUid;
+    public final String packageName;
+    public final String sourcePackage;
+    public int count;
+    public long when;
+    public long windowLength;
+    public long whenElapsed;    // 'when' in the elapsed time base
+    public long maxWhenElapsed; // also in the elapsed time base
+    // Expected alarm expiry time before app standby deferring is applied.
+    public long expectedWhenElapsed;
+    public long expectedMaxWhenElapsed;
+    public long repeatInterval;
+    public AlarmManagerService.PriorityClass priorityClass;
+
+    Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
+            long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag,
+            WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info,
+            int _uid, String _pkgName) {
+        type = _type;
+        origWhen = _when;
+        wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
+                || _type == AlarmManager.RTC_WAKEUP;
+        when = _when;
+        whenElapsed = _whenElapsed;
+        expectedWhenElapsed = _whenElapsed;
+        windowLength = _windowLength;
+        maxWhenElapsed = expectedMaxWhenElapsed = AlarmManagerService.clampPositive(_maxWhen);
+        repeatInterval = _interval;
+        operation = _op;
+        listener = _rec;
+        listenerTag = _listenerTag;
+        statsTag = makeTag(_op, _listenerTag, _type);
+        workSource = _ws;
+        flags = _flags;
+        alarmClock = _info;
+        uid = _uid;
+        packageName = _pkgName;
+        sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
+        creatorUid = (operation != null) ? operation.getCreatorUid() : uid;
+    }
+
+    public static String makeTag(PendingIntent pi, String tag, int type) {
+        final String alarmString = type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
+                ? "*walarm*:" : "*alarm*:";
+        return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag);
+    }
+
+    public AlarmManagerService.WakeupEvent makeWakeupEvent(long nowRTC) {
+        return new AlarmManagerService.WakeupEvent(nowRTC, creatorUid,
+                (operation != null)
+                    ? operation.getIntent().getAction()
+                    : ("<listener>:" + listenerTag));
+    }
+
+    // Returns true if either matches
+    public boolean matches(PendingIntent pi, IAlarmListener rec) {
+        return (operation != null)
+                ? operation.equals(pi)
+                : rec != null && listener.asBinder().equals(rec.asBinder());
+    }
+
+    public boolean matches(String packageName) {
+        return packageName.equals(sourcePackage);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Alarm{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" type ");
+        sb.append(type);
+        sb.append(" when ");
+        sb.append(when);
+        sb.append(" ");
+        sb.append(" whenElapsed ");
+        sb.append(whenElapsed);
+        sb.append(" ");
+        sb.append(sourcePackage);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /**
+     * @deprecated Use {{@link #dump(IndentingPrintWriter, long, SimpleDateFormat)}} instead.
+     */
+    @Deprecated
+    public void dump(PrintWriter pw, String prefix, long nowELAPSED, SimpleDateFormat sdf) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix);
+        dump(ipw, nowELAPSED, sdf);
+    }
+
+    public void dump(IndentingPrintWriter ipw, long nowELAPSED, SimpleDateFormat sdf) {
+        final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
+        ipw.print("tag=");
+        ipw.println(statsTag);
+
+        ipw.print("type=");
+        ipw.print(type);
+        ipw.print(" expectedWhenElapsed=");
+        TimeUtils.formatDuration(expectedWhenElapsed, nowELAPSED, ipw);
+        ipw.print(" expectedMaxWhenElapsed=");
+        TimeUtils.formatDuration(expectedMaxWhenElapsed, nowELAPSED, ipw);
+        ipw.print(" whenElapsed=");
+        TimeUtils.formatDuration(whenElapsed, nowELAPSED, ipw);
+        ipw.print(" maxWhenElapsed=");
+        TimeUtils.formatDuration(maxWhenElapsed, nowELAPSED, ipw);
+        ipw.print(" when=");
+        if (isRtc) {
+            ipw.print(sdf.format(new Date(when)));
+        } else {
+            TimeUtils.formatDuration(when, nowELAPSED, ipw);
+        }
+        ipw.println();
+
+        ipw.print("window=");
+        TimeUtils.formatDuration(windowLength, ipw);
+        ipw.print(" repeatInterval=");
+        ipw.print(repeatInterval);
+        ipw.print(" count=");
+        ipw.print(count);
+        ipw.print(" flags=0x");
+        ipw.println(Integer.toHexString(flags));
+
+        if (alarmClock != null) {
+            ipw.println("Alarm clock:");
+
+            ipw.print("  triggerTime=");
+            ipw.println(sdf.format(new Date(alarmClock.getTriggerTime())));
+
+            ipw.print("  showIntent=");
+            ipw.println(alarmClock.getShowIntent());
+        }
+        ipw.print("operation=");
+        ipw.println(operation);
+
+        if (listener != null) {
+            ipw.print("listener=");
+            ipw.println(listener.asBinder());
+        }
+    }
+
+    public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) {
+        final long token = proto.start(fieldId);
+
+        proto.write(AlarmProto.TAG, statsTag);
+        proto.write(AlarmProto.TYPE, type);
+        proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
+        proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
+        proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
+        proto.write(AlarmProto.COUNT, count);
+        proto.write(AlarmProto.FLAGS, flags);
+        if (alarmClock != null) {
+            alarmClock.dumpDebug(proto, AlarmProto.ALARM_CLOCK);
+        }
+        if (operation != null) {
+            operation.dumpDebug(proto, AlarmProto.OPERATION);
+        }
+        if (listener != null) {
+            proto.write(AlarmProto.LISTENER, listener.asBinder().toString());
+        }
+
+        proto.end(token);
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e2f13c560..ec7e99e 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -75,6 +75,7 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.LongArrayQueue;
@@ -91,10 +92,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.LocalLog;
 import com.android.internal.util.StatLogger;
 import com.android.server.AlarmManagerInternal;
@@ -109,7 +108,6 @@
 import com.android.server.usage.AppStandbyInternal;
 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
-import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
@@ -121,7 +119,6 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Random;
 import java.util.TimeZone;
@@ -132,23 +129,18 @@
  * Alarm manager implementation.
  *
  * Unit test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java
+ * atest FrameworksMockingServicesTests:com.android.server.alarm.AlarmManagerServiceTest
  */
 public class AlarmManagerService extends SystemService {
     private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
-    private static final int RTC_MASK = 1 << RTC;
     private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
-    private static final int ELAPSED_REALTIME_MASK = 1 << ELAPSED_REALTIME;
     static final int TIME_CHANGED_MASK = 1 << 16;
-    static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK;
-
-    // Mask for testing whether a given alarm type is wakeup vs non-wakeup
-    static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup
+    static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK | ELAPSED_REALTIME_WAKEUP_MASK;
 
     static final String TAG = "AlarmManager";
+    static final String TIME_TICK_TAG = "TIME_TICK";
     static final boolean localLOGV = false;
     static final boolean DEBUG_BATCH = localLOGV || false;
-    static final boolean DEBUG_VALIDATE = localLOGV || false;
     static final boolean DEBUG_ALARM_CLOCK = localLOGV || false;
     static final boolean DEBUG_LISTENER_CALLBACK = localLOGV || false;
     static final boolean DEBUG_WAKELOCK = localLOGV || false;
@@ -170,9 +162,6 @@
 
     private final Intent mBackgroundIntent
             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
-    static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder();
-
-    static final boolean WAKEUP_STATS = false;
 
     private static final Intent NEXT_ALARM_CLOCK_CHANGED_INTENT =
             new Intent(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)
@@ -199,8 +188,6 @@
 
     private long mLastTickSet;
     private long mLastTickReceived;
-    private long mLastTickAdded;
-    private long mLastTickRemoved;
     // ring buffer of recent TIME_TICK issuance, in the elapsed timebase
     private final long[] mTickHistory = new long[TICK_HISTORY_DEPTH];
     private int mNextTickHistory;
@@ -250,6 +237,10 @@
      */
     final SparseBooleanArray mUseAllowWhileIdleShortTime = new SparseBooleanArray();
 
+    static boolean isTimeTickAlarm(Alarm a) {
+        return a.uid == Process.SYSTEM_UID && TIME_TICK_TAG.equals(a.listenerTag);
+    }
+
     final static class IdleDispatchEntry {
         int uid;
         String pkg;
@@ -261,12 +252,10 @@
     final ArrayList<IdleDispatchEntry> mAllowWhileIdleDispatches = new ArrayList();
 
     interface Stats {
-        int REBATCH_ALL_ALARMS = 0;
         int REORDER_ALARMS_FOR_STANDBY = 1;
     }
 
-    private final StatLogger mStatLogger = new StatLogger(new String[] {
-            "REBATCH_ALL_ALARMS",
+    private final StatLogger mStatLogger = new StatLogger("Alarm manager stats", new String[]{
             "REORDER_ALARMS_FOR_STANDBY",
     });
 
@@ -342,7 +331,7 @@
 
         /**
          * @param n The desired nth-last wakeup
-         *                (1=1st-last=the ultimate wakeup and 2=2nd-last=the penultimate wakeup)
+         *          (1=1st-last=the ultimate wakeup and 2=2nd-last=the penultimate wakeup)
          */
         long getNthLastWakeupForPackage(String packageName, int userId, int n) {
             final LongArrayQueue history = mPackageHistory.get(Pair.create(packageName, userId));
@@ -423,8 +412,8 @@
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
         private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
         private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY;
-        private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000;
-        private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
+        private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9 * 60 * 1000;
+        private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
         private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
         private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
         private static final long DEFAULT_APP_STANDBY_WINDOW = 60 * 60 * 1000;  // 1 hr
@@ -571,50 +560,62 @@
 
             pw.increaseIndent();
 
-            pw.print(KEY_MIN_FUTURITY); pw.print("=");
+            pw.print(KEY_MIN_FUTURITY);
+            pw.print("=");
             TimeUtils.formatDuration(MIN_FUTURITY, pw);
             pw.println();
 
-            pw.print(KEY_MIN_INTERVAL); pw.print("=");
+            pw.print(KEY_MIN_INTERVAL);
+            pw.print("=");
             TimeUtils.formatDuration(MIN_INTERVAL, pw);
             pw.println();
 
-            pw.print(KEY_MAX_INTERVAL); pw.print("=");
+            pw.print(KEY_MAX_INTERVAL);
+            pw.print("=");
             TimeUtils.formatDuration(MAX_INTERVAL, pw);
             pw.println();
 
-            pw.print(KEY_LISTENER_TIMEOUT); pw.print("=");
+            pw.print(KEY_LISTENER_TIMEOUT);
+            pw.print("=");
             TimeUtils.formatDuration(LISTENER_TIMEOUT, pw);
             pw.println();
 
-            pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME); pw.print("=");
+            pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME);
+            pw.print("=");
             TimeUtils.formatDuration(ALLOW_WHILE_IDLE_SHORT_TIME, pw);
             pw.println();
 
-            pw.print(KEY_ALLOW_WHILE_IDLE_LONG_TIME); pw.print("=");
+            pw.print(KEY_ALLOW_WHILE_IDLE_LONG_TIME);
+            pw.print("=");
             TimeUtils.formatDuration(ALLOW_WHILE_IDLE_LONG_TIME, pw);
             pw.println();
 
-            pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION); pw.print("=");
+            pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+            pw.print("=");
             TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
             pw.println();
 
-            pw.print(KEY_MAX_ALARMS_PER_UID); pw.print("=");
+            pw.print(KEY_MAX_ALARMS_PER_UID);
+            pw.print("=");
             pw.println(MAX_ALARMS_PER_UID);
 
-            pw.print(KEY_APP_STANDBY_WINDOW); pw.print("=");
+            pw.print(KEY_APP_STANDBY_WINDOW);
+            pw.print("=");
             TimeUtils.formatDuration(APP_STANDBY_WINDOW, pw);
             pw.println();
 
             for (int i = 0; i < KEYS_APP_STANDBY_QUOTAS.length; i++) {
-                pw.print(KEYS_APP_STANDBY_QUOTAS[i]); pw.print("=");
+                pw.print(KEYS_APP_STANDBY_QUOTAS[i]);
+                pw.print("=");
                 pw.println(APP_STANDBY_QUOTAS[i]);
             }
 
-            pw.print(KEY_APP_STANDBY_RESTRICTED_QUOTA); pw.print("=");
+            pw.print(KEY_APP_STANDBY_RESTRICTED_QUOTA);
+            pw.print("=");
             pw.println(APP_STANDBY_RESTRICTED_QUOTA);
 
-            pw.print(KEY_APP_STANDBY_RESTRICTED_WINDOW); pw.print("=");
+            pw.print(KEY_APP_STANDBY_RESTRICTED_WINDOW);
+            pw.print("=");
             TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_WINDOW, pw);
             pw.println();
 
@@ -671,182 +672,6 @@
         }
     }
 
-    final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>();
-    final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day
-
-    final class Batch {
-        long start;     // These endpoints are always in ELAPSED
-        long end;
-        int flags;      // Flags for alarms, such as FLAG_STANDALONE.
-
-        final ArrayList<Alarm> alarms = new ArrayList<Alarm>();
-
-        Batch(Alarm seed) {
-            start = seed.whenElapsed;
-            end = clampPositive(seed.maxWhenElapsed);
-            flags = seed.flags;
-            alarms.add(seed);
-            if (seed.listener == mTimeTickTrigger) {
-                mLastTickAdded = mInjector.getCurrentTimeMillis();
-            }
-        }
-
-        int size() {
-            return alarms.size();
-        }
-
-        Alarm get(int index) {
-            return alarms.get(index);
-        }
-
-        boolean canHold(long whenElapsed, long maxWhen) {
-            return (end >= whenElapsed) && (start <= maxWhen);
-        }
-
-        boolean add(Alarm alarm) {
-            boolean newStart = false;
-            // narrows the batch if necessary; presumes that canHold(alarm) is true
-            int index = Collections.binarySearch(alarms, alarm, sIncreasingTimeOrder);
-            if (index < 0) {
-                index = 0 - index - 1;
-            }
-            alarms.add(index, alarm);
-            if (alarm.listener == mTimeTickTrigger) {
-                mLastTickAdded = mInjector.getCurrentTimeMillis();
-            }
-            if (DEBUG_BATCH) {
-                Slog.v(TAG, "Adding " + alarm + " to " + this);
-            }
-            if (alarm.whenElapsed > start) {
-                start = alarm.whenElapsed;
-                newStart = true;
-            }
-            if (alarm.maxWhenElapsed < end) {
-                end = alarm.maxWhenElapsed;
-            }
-            flags |= alarm.flags;
-
-            if (DEBUG_BATCH) {
-                Slog.v(TAG, "    => now " + this);
-            }
-            return newStart;
-        }
-
-        /**
-         * Remove an alarm from this batch.
-         * <p> <b> Should be used only while re-ordering the alarm within the service </b> as it
-         * does not update {@link #mAlarmsPerUid}
-         */
-        boolean remove(Alarm alarm) {
-            return remove(a -> (a == alarm), true);
-        }
-
-        boolean remove(Predicate<Alarm> predicate, boolean reOrdering) {
-            boolean didRemove = false;
-            long newStart = 0;  // recalculate endpoints as we go
-            long newEnd = Long.MAX_VALUE;
-            int newFlags = 0;
-            for (int i = 0; i < alarms.size(); ) {
-                Alarm alarm = alarms.get(i);
-                if (predicate.test(alarm)) {
-                    alarms.remove(i);
-                    if (!reOrdering) {
-                        decrementAlarmCount(alarm.uid, 1);
-                    }
-                    didRemove = true;
-                    if (alarm.alarmClock != null) {
-                        mNextAlarmClockMayChange = true;
-                    }
-                    if (alarm.listener == mTimeTickTrigger) {
-                        mLastTickRemoved = mInjector.getCurrentTimeMillis();
-                    }
-                } else {
-                    if (alarm.whenElapsed > newStart) {
-                        newStart = alarm.whenElapsed;
-                    }
-                    if (alarm.maxWhenElapsed < newEnd) {
-                        newEnd = alarm.maxWhenElapsed;
-                    }
-                    newFlags |= alarm.flags;
-                    i++;
-                }
-            }
-            if (didRemove) {
-                // commit the new batch bounds
-                start = newStart;
-                end = newEnd;
-                flags = newFlags;
-            }
-            return didRemove;
-        }
-
-        boolean hasPackage(final String packageName) {
-            final int N = alarms.size();
-            for (int i = 0; i < N; i++) {
-                Alarm a = alarms.get(i);
-                if (a.matches(packageName)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        boolean hasWakeups() {
-            final int N = alarms.size();
-            for (int i = 0; i < N; i++) {
-                Alarm a = alarms.get(i);
-                // non-wakeup alarms are types 1 and 3, i.e. have the low bit set
-                if ((a.type & TYPE_NONWAKEUP_MASK) == 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder b = new StringBuilder(40);
-            b.append("Batch{"); b.append(Integer.toHexString(this.hashCode()));
-            b.append(" num="); b.append(size());
-            b.append(" start="); b.append(start);
-            b.append(" end="); b.append(end);
-            if (flags != 0) {
-                b.append(" flgs=0x");
-                b.append(Integer.toHexString(flags));
-            }
-            b.append('}');
-            return b.toString();
-        }
-
-        public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed,
-                long nowRTC) {
-            final long token = proto.start(fieldId);
-
-            proto.write(BatchProto.START_REALTIME, start);
-            proto.write(BatchProto.END_REALTIME, end);
-            proto.write(BatchProto.FLAGS, flags);
-            for (Alarm a : alarms) {
-                a.dumpDebug(proto, BatchProto.ALARMS, nowElapsed, nowRTC);
-            }
-
-            proto.end(token);
-        }
-    }
-
-    static class BatchTimeOrder implements Comparator<Batch> {
-        public int compare(Batch b1, Batch b2) {
-            long when1 = b1.start;
-            long when2 = b2.start;
-            if (when1 > when2) {
-                return 1;
-            }
-            if (when1 < when2) {
-                return -1;
-            }
-            return 0;
-        }
-    }
-
     final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() {
         @Override
         public int compare(Alarm lhs, Alarm rhs) {
@@ -909,8 +734,8 @@
 
     // minimum recurrence period or alarm futurity for us to be able to fuzz it
     static final long MIN_FUZZABLE_INTERVAL = 10000;
-    static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
-    final ArrayList<Batch> mAlarmBatches = new ArrayList<>();
+    @GuardedBy("mLock")
+    final AlarmStore mAlarmStore;
 
     // set to non-null if in idle mode; while in this mode, any alarms we don't want
     // to run during this time are placed in mPendingWhileIdleAlarms
@@ -922,6 +747,7 @@
     AlarmManagerService(Context context, Injector injector) {
         super(context);
         mInjector = injector;
+        mAlarmStore = new BatchingAlarmStore(() -> mNextAlarmClockMayChange = true);
     }
 
     public AlarmManagerService(Context context) {
@@ -949,171 +775,45 @@
         if (futurity < MIN_FUZZABLE_INTERVAL) {
             futurity = 0;
         }
-        return clampPositive(triggerAtTime + (long)(.75 * futurity));
+        return clampPositive(triggerAtTime + (long) (.75 * futurity));
     }
 
-    // returns true if the batch was added at the head
-    static boolean addBatchLocked(ArrayList<Batch> list, Batch newBatch) {
-        int index = Collections.binarySearch(list, newBatch, sBatchOrder);
-        if (index < 0) {
-            index = 0 - index - 1;
-        }
-        list.add(index, newBatch);
-        return (index == 0);
-    }
-
-    private void insertAndBatchAlarmLocked(Alarm alarm) {
-        final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
-                : attemptCoalesceLocked(alarm.whenElapsed, alarm.maxWhenElapsed);
-
-        if (whichBatch < 0) {
-            addBatchLocked(mAlarmBatches, new Batch(alarm));
-        } else {
-            final Batch batch = mAlarmBatches.get(whichBatch);
-            if (batch.add(alarm)) {
-                // The start time of this batch advanced, so batch ordering may
-                // have just been broken.  Move it to where it now belongs.
-                mAlarmBatches.remove(whichBatch);
-                addBatchLocked(mAlarmBatches, batch);
-            }
-        }
-    }
-
-    // Return the index of the matching batch, or -1 if none found.
-    int attemptCoalesceLocked(long whenElapsed, long maxWhen) {
-        final int N = mAlarmBatches.size();
-        for (int i = 0; i < N; i++) {
-            Batch b = mAlarmBatches.get(i);
-            if ((b.flags&AlarmManager.FLAG_STANDALONE) == 0 && b.canHold(whenElapsed, maxWhen)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-    /** @return total count of the alarms in a set of alarm batches. */
-    static int getAlarmCount(ArrayList<Batch> batches) {
-        int ret = 0;
-
-        final int size = batches.size();
-        for (int i = 0; i < size; i++) {
-            ret += batches.get(i).size();
-        }
-        return ret;
-    }
-
-    boolean haveAlarmsTimeTickAlarm(ArrayList<Alarm> alarms) {
-        if (alarms.size() == 0) {
-            return false;
-        }
-        final int batchSize = alarms.size();
-        for (int j = 0; j < batchSize; j++) {
-            if (alarms.get(j).listener == mTimeTickTrigger) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean haveBatchesTimeTickAlarm(ArrayList<Batch> batches) {
-        final int numBatches = batches.size();
-        for (int i = 0; i < numBatches; i++) {
-            if (haveAlarmsTimeTickAlarm(batches.get(i).alarms)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // The RTC clock has moved arbitrarily, so we need to recalculate all the batching
-    void rebatchAllAlarms() {
+    // The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
+    void reevaluateRtcAlarms(final long nowElapsed) {
         synchronized (mLock) {
-            rebatchAllAlarmsLocked(true);
-        }
-    }
-
-    void rebatchAllAlarmsLocked(boolean doValidate) {
-        final long start = mStatLogger.getTime();
-        final int oldCount =
-                getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms);
-        final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches)
-                || haveAlarmsTimeTickAlarm(mPendingWhileIdleAlarms);
-
-        ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone();
-        mAlarmBatches.clear();
-        Alarm oldPendingIdleUntil = mPendingIdleUntil;
-        final long nowElapsed = mInjector.getElapsedRealtime();
-        final int oldBatches = oldSet.size();
-        for (int batchNum = 0; batchNum < oldBatches; batchNum++) {
-            Batch batch = oldSet.get(batchNum);
-            final int N = batch.size();
-            for (int i = 0; i < N; i++) {
-                reAddAlarmLocked(batch.get(i), nowElapsed, doValidate);
+            final ArrayList<Alarm> rtcAlarms = mAlarmStore.remove(a -> (a.type == RTC
+                    || a.type == RTC_WAKEUP));
+            for (final Alarm a : rtcAlarms) {
+                restoreAlarmLocked(a, nowElapsed);
+                setImplLocked(a);
             }
         }
-        if (oldPendingIdleUntil != null && oldPendingIdleUntil != mPendingIdleUntil) {
-            Slog.wtf(TAG, "Rebatching: idle until changed from " + oldPendingIdleUntil
-                    + " to " + mPendingIdleUntil);
-            if (mPendingIdleUntil == null) {
-                // Somehow we lost this...  we need to restore all of the pending alarms.
-                restorePendingWhileIdleAlarmsLocked();
-            }
-        }
-        final int newCount =
-                getAlarmCount(mAlarmBatches) + ArrayUtils.size(mPendingWhileIdleAlarms);
-        final boolean newHasTick = haveBatchesTimeTickAlarm(mAlarmBatches)
-                || haveAlarmsTimeTickAlarm(mPendingWhileIdleAlarms);
-
-        if (oldCount != newCount) {
-            Slog.wtf(TAG, "Rebatching: total count changed from " + oldCount + " to " + newCount);
-        }
-        if (oldHasTick != newHasTick) {
-            Slog.wtf(TAG, "Rebatching: hasTick changed from " + oldHasTick + " to " + newHasTick);
-        }
-
-        rescheduleKernelAlarmsLocked();
-        updateNextAlarmClockLocked();
-        mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start);
     }
 
     /**
-     * Re-orders the alarm batches based on newly evaluated send times based on the current
-     * app-standby buckets
+     * Recalculates alarm send times based on the current app-standby buckets
+     *
      * @param targetPackages [Package, User] pairs for which alarms need to be re-evaluated,
      *                       null indicates all
      * @return True if there was any reordering done to the current list.
      */
     boolean reorderAlarmsBasedOnStandbyBuckets(ArraySet<Pair<String, Integer>> targetPackages) {
         final long start = mStatLogger.getTime();
-        final ArrayList<Alarm> rescheduledAlarms = new ArrayList<>();
 
-        for (int batchIndex = mAlarmBatches.size() - 1; batchIndex >= 0; batchIndex--) {
-            final Batch batch = mAlarmBatches.get(batchIndex);
-            for (int alarmIndex = batch.size() - 1; alarmIndex >= 0; alarmIndex--) {
-                final Alarm alarm = batch.get(alarmIndex);
-                final Pair<String, Integer> packageUser =
-                        Pair.create(alarm.sourcePackage, UserHandle.getUserId(alarm.creatorUid));
-                if (targetPackages != null && !targetPackages.contains(packageUser)) {
-                    continue;
-                }
-                if (adjustDeliveryTimeBasedOnBucketLocked(alarm)) {
-                    batch.remove(alarm);
-                    rescheduledAlarms.add(alarm);
-                }
+        final boolean changed = mAlarmStore.recalculateAlarmDeliveries(a -> {
+            final Pair<String, Integer> packageUser =
+                    Pair.create(a.sourcePackage, UserHandle.getUserId(a.creatorUid));
+            if (targetPackages != null && !targetPackages.contains(packageUser)) {
+                return false;
             }
-            if (batch.size() == 0) {
-                mAlarmBatches.remove(batchIndex);
-            }
-        }
-        for (int i = 0; i < rescheduledAlarms.size(); i++) {
-            final Alarm a = rescheduledAlarms.get(i);
-            insertAndBatchAlarmLocked(a);
-        }
+            return adjustDeliveryTimeBasedOnBucketLocked(a);
+        });
 
         mStatLogger.logDurationStat(Stats.REORDER_ALARMS_FOR_STANDBY, start);
-        return rescheduledAlarms.size() > 0;
+        return changed;
     }
 
-    void reAddAlarmLocked(Alarm a, long nowElapsed, boolean doValidate) {
+    private void restoreAlarmLocked(Alarm a, long nowElapsed) {
         a.when = a.origWhen;
         long whenElapsed = convertToElapsed(a.when, a.type);
         final long maxElapsed;
@@ -1130,7 +830,6 @@
         }
         a.expectedWhenElapsed = a.whenElapsed = whenElapsed;
         a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed;
-        setImplLocked(a, true, doValidate);
     }
 
     static long clampPositive(long val) {
@@ -1141,7 +840,7 @@
      * Sends alarms that were blocked due to user applied background restrictions - either because
      * the user lifted those or the uid came to foreground.
      *
-     * @param uid uid to filter on
+     * @param uid         uid to filter on
      * @param packageName package to filter on, or null for all packages in uid
      */
     void sendPendingBackgroundAlarmsLocked(int uid, String packageName) {
@@ -1236,7 +935,7 @@
                 final long nextElapsed = alarm.expectedWhenElapsed + delta;
                 setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
                         maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
-                        alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
+                        alarm.repeatInterval, alarm.operation, null, null, alarm.flags,
                         alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
                 // Kernel alarms will be rescheduled as needed in setImplLocked
             }
@@ -1246,7 +945,7 @@
             if (mPendingNonWakeupAlarms.size() == 0) {
                 mStartCurrentDelayTime = nowELAPSED;
                 mNextNonWakeupDeliveryTime = nowELAPSED
-                        + ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
+                        + ((currentNonWakeupFuzzLocked(nowELAPSED) * 3) / 2);
             }
             mPendingNonWakeupAlarms.addAll(alarms);
             mNumDelayedAlarms += alarms.size();
@@ -1284,9 +983,10 @@
             ArrayList<Alarm> alarms = mPendingWhileIdleAlarms;
             mPendingWhileIdleAlarms = new ArrayList<>();
             final long nowElapsed = mInjector.getElapsedRealtime();
-            for (int i=alarms.size() - 1; i >= 0; i--) {
+            for (int i = alarms.size() - 1; i >= 0; i--) {
                 Alarm a = alarms.get(i);
-                reAddAlarmLocked(a, nowElapsed, false);
+                restoreAlarmLocked(a, nowElapsed);
+                setImplLocked(a);
             }
         }
 
@@ -1511,7 +1211,7 @@
             // Ensure that we're booting with a halfway sensible current time.  Use the
             // most recent of Build.TIME, the root file system's timestamp, and the
             // value of the ro.build.date.utc system property (which is in seconds).
-            final long systemBuildTime =  Long.max(
+            final long systemBuildTime = Long.max(
                     1000L * SystemProperties.getLong("ro.build.date.utc", -1L),
                     Long.max(Environment.getRootDirectory().lastModified(), Build.TIME));
             if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
@@ -1529,8 +1229,8 @@
 
             mTimeTickIntent = new Intent(Intent.ACTION_TIME_TICK).addFlags(
                     Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                    | Intent.FLAG_RECEIVER_FOREGROUND
-                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+                            | Intent.FLAG_RECEIVER_FOREGROUND
+                            | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
 
             mTimeTickTrigger = new IAlarmListener.Stub() {
                 @Override
@@ -1688,7 +1388,6 @@
             PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
             int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock,
             int callingUid, String callingPackage) {
-        // must be *either* PendingIntent or AlarmReceiver, but not both
         if ((operation == null && directReceiver == null)
                 || (operation != null && directReceiver != null)) {
             Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver");
@@ -1720,7 +1419,7 @@
         final long minInterval = mConstants.MIN_INTERVAL;
         if (interval > 0 && interval < minInterval) {
             Slog.w(TAG, "Suspiciously short interval " + interval
-                    + " millis; expanding to " + (minInterval/1000)
+                    + " millis; expanding to " + (minInterval / 1000)
                     + " seconds");
             interval = minInterval;
         } else if (interval > mConstants.MAX_INTERVAL) {
@@ -1773,14 +1472,14 @@
                 throw new IllegalStateException(errorMsg);
             }
             setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
-                    interval, operation, directReceiver, listenerTag, flags, true, workSource,
+                    interval, operation, directReceiver, listenerTag, flags, workSource,
                     alarmClock, callingUid, callingPackage);
         }
     }
 
     private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
             long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver,
-            String listenerTag, int flags, boolean doValidate, WorkSource workSource,
+            String listenerTag, int flags, WorkSource workSource,
             AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
         Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
                 operation, directReceiver, listenerTag, workSource, flags, alarmClock,
@@ -1792,7 +1491,7 @@
         }
         removeLocked(operation, directReceiver);
         incrementAlarmCount(a.uid);
-        setImplLocked(a, false, doValidate);
+        setImplLocked(a);
     }
 
     /**
@@ -1817,7 +1516,46 @@
     }
 
     /**
+     * Adjusts the idle-until alarm delivery time based on the upcoming wake-from-idle alarm.
+     *
+     * @param alarm The alarm to adjust
+     * @return true if the alarm delivery time was updated.
+     */
+    private boolean adjustIdleUntilTime(Alarm alarm) {
+        if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+            return false;
+        }
+        // This is a special alarm that will put the system into idle until it goes off.
+        // The caller has given the time they want this to happen at, however we need
+        // to pull that earlier if there are existing alarms that have requested to
+        // bring us out of idle at an earlier time.
+        if (mNextWakeFromIdle != null && alarm.whenElapsed > mNextWakeFromIdle.whenElapsed) {
+            alarm.when = alarm.whenElapsed = alarm.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
+        }
+        // Add fuzz to make the alarm go off some time before the actual desired time.
+        final long nowElapsed = mInjector.getElapsedRealtime();
+        final int fuzz = fuzzForDuration(alarm.whenElapsed - nowElapsed);
+        if (fuzz > 0) {
+            if (mRandom == null) {
+                mRandom = new Random();
+            }
+            final int delta = mRandom.nextInt(fuzz);
+            alarm.whenElapsed -= delta;
+            if (false) {
+                Slog.d(TAG, "Alarm when: " + alarm.whenElapsed);
+                Slog.d(TAG, "Delta until alarm: " + (alarm.whenElapsed - nowElapsed));
+                Slog.d(TAG, "Applied fuzz: " + fuzz);
+                Slog.d(TAG, "Final delta: " + delta);
+                Slog.d(TAG, "Final when: " + alarm.whenElapsed);
+            }
+            alarm.when = alarm.maxWhenElapsed = alarm.whenElapsed;
+        }
+        return true;
+    }
+
+    /**
      * Adjusts the alarm delivery time based on the current app standby bucket.
+     *
      * @param alarm The alarm to adjust
      * @return true if the alarm delivery time was updated.
      */
@@ -1892,47 +1630,17 @@
         return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
     }
 
-    private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) {
-        if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
-            // This is a special alarm that will put the system into idle until it goes off.
-            // The caller has given the time they want this to happen at, however we need
-            // to pull that earlier if there are existing alarms that have requested to
-            // bring us out of idle at an earlier time.
-            if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) {
-                a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
-            }
-            // Add fuzz to make the alarm go off some time before the actual desired time.
-            final long nowElapsed = mInjector.getElapsedRealtime();
-            final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
-            if (fuzz > 0) {
-                if (mRandom == null) {
-                    mRandom = new Random();
-                }
-                final int delta = mRandom.nextInt(fuzz);
-                a.whenElapsed -= delta;
-                if (false) {
-                    Slog.d(TAG, "Alarm when: " + a.whenElapsed);
-                    Slog.d(TAG, "Delta until alarm: " + (a.whenElapsed-nowElapsed));
-                    Slog.d(TAG, "Applied fuzz: " + fuzz);
-                    Slog.d(TAG, "Final delta: " + delta);
-                    Slog.d(TAG, "Final when: " + a.whenElapsed);
-                }
-                a.when = a.maxWhenElapsed = a.whenElapsed;
-            }
+    private static boolean isAllowedWhileIdle(Alarm a) {
+        return ((a.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE
+                | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
+                | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0);
+    }
 
-        } else if (mPendingIdleUntil != null) {
-            // We currently have an idle until alarm scheduled; if the new alarm has
-            // not explicitly stated it wants to run while idle, then put it on hold.
-            if ((a.flags&(AlarmManager.FLAG_ALLOW_WHILE_IDLE
-                    | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
-                    | AlarmManager.FLAG_WAKE_FROM_IDLE))
-                    == 0) {
-                mPendingWhileIdleAlarms.add(a);
-                return;
-            }
-        }
-        if (RECORD_DEVICE_IDLE_ALARMS) {
-            if ((a.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+    private void setImplLocked(Alarm a) {
+        if ((a.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+            adjustIdleUntilTime(a);
+
+            if (RECORD_DEVICE_IDLE_ALARMS) {
                 IdleDispatchEntry ent = new IdleDispatchEntry();
                 ent.uid = a.uid;
                 ent.pkg = a.operation.getCreatorPackage();
@@ -1941,67 +1649,55 @@
                 ent.elapsedRealtime = mInjector.getElapsedRealtime();
                 ent.argRealtime = a.whenElapsed;
                 mAllowWhileIdleDispatches.add(ent);
-            }
-        }
-        adjustDeliveryTimeBasedOnBucketLocked(a);
-        insertAndBatchAlarmLocked(a);
-
-        if (a.alarmClock != null) {
-            mNextAlarmClockMayChange = true;
-        }
-
-        boolean needRebatch = false;
-
-        if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
-            if (RECORD_DEVICE_IDLE_ALARMS) {
                 if (mPendingIdleUntil == null) {
-                    IdleDispatchEntry ent = new IdleDispatchEntry();
-                    ent.uid = 0;
-                    ent.pkg = "START IDLE";
-                    ent.elapsedRealtime = mInjector.getElapsedRealtime();
-                    mAllowWhileIdleDispatches.add(ent);
+                    IdleDispatchEntry ent2 = new IdleDispatchEntry();
+                    ent2.uid = 0;
+                    ent2.pkg = "START IDLE";
+                    ent2.elapsedRealtime = mInjector.getElapsedRealtime();
+                    mAllowWhileIdleDispatches.add(ent2);
                 }
             }
             if ((mPendingIdleUntil != a) && (mPendingIdleUntil != null)) {
                 Slog.wtfStack(TAG, "setImplLocked: idle until changed from " + mPendingIdleUntil
                         + " to " + a);
             }
-
             mPendingIdleUntil = a;
-            needRebatch = true;
-        } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+            final ArrayList<Alarm> notAllowedWhileIdleAlarms = mAlarmStore.remove(
+                    alarm -> !isAllowedWhileIdle(alarm));
+            mPendingWhileIdleAlarms.addAll(notAllowedWhileIdleAlarms);
+        } else if (mPendingIdleUntil != null) {
+            // We currently have an idle until alarm scheduled; if the new alarm has
+            // not explicitly stated it wants to run while idle, then put it on hold.
+            if (!isAllowedWhileIdle(a)) {
+                mPendingWhileIdleAlarms.add(a);
+                return;
+            }
+        }
+        if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
             if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
                 mNextWakeFromIdle = a;
                 // If this wake from idle is earlier than whatever was previously scheduled,
                 // and we are currently idling, then we need to rebatch alarms in case the idle
                 // until time needs to be updated.
                 if (mPendingIdleUntil != null) {
-                    needRebatch = true;
+                    final long nowElapsed = mInjector.getElapsedRealtime();
+                    mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+                        if (alarm != mPendingIdleUntil) {
+                            return false;
+                        }
+                        restoreAlarmLocked(alarm, nowElapsed);
+                        return adjustIdleUntilTime(alarm);
+                    });
                 }
             }
         }
-
-        if (!rebatching) {
-            if (DEBUG_VALIDATE) {
-                if (doValidate && !validateConsistencyLocked()) {
-                    Slog.v(TAG, "Tipping-point operation: type=" + a.type + " when=" + a.when
-                            + " when(hex)=" + Long.toHexString(a.when)
-                            + " whenElapsed=" + a.whenElapsed
-                            + " maxWhenElapsed=" + a.maxWhenElapsed
-                            + " interval=" + a.repeatInterval + " op=" + a.operation
-                            + " flags=0x" + Integer.toHexString(a.flags));
-                    rebatchAllAlarmsLocked(false);
-                    needRebatch = false;
-                }
-            }
-
-            if (needRebatch) {
-                rebatchAllAlarmsLocked(false);
-            }
-
-            rescheduleKernelAlarmsLocked();
-            updateNextAlarmClockLocked();
+        if (a.alarmClock != null) {
+            mNextAlarmClockMayChange = true;
         }
+        adjustDeliveryTimeBasedOnBucketLocked(a);
+        mAlarmStore.add(a);
+        rescheduleKernelAlarmsLocked();
+        updateNextAlarmClockLocked();
     }
 
     /**
@@ -2051,7 +1747,8 @@
             // Repeating alarms must use PendingIntent, not direct listener
             if (interval != 0) {
                 if (directReceiver != null) {
-                    throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers");
+                    throw new IllegalArgumentException(
+                            "Repeating alarms cannot use AlarmReceivers");
                 }
             }
 
@@ -2194,17 +1891,27 @@
             final long nowRTC = mInjector.getCurrentTimeMillis();
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 
-            pw.print("  nowRTC="); pw.print(nowRTC);
-            pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
-            pw.print(" nowELAPSED="); pw.print(nowELAPSED);
+            pw.print("  nowRTC=");
+            pw.print(nowRTC);
+            pw.print("=");
+            pw.print(sdf.format(new Date(nowRTC)));
+            pw.print(" nowELAPSED=");
+            pw.print(nowELAPSED);
             pw.println();
-            pw.print("  mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
-            pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
-            pw.print("  mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime);
-            pw.print("  mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived)));
-            pw.print("  mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet)));
-            pw.print("  mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded)));
-            pw.print("  mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved)));
+
+            pw.print("  mLastTimeChangeClockTime=");
+            pw.print(mLastTimeChangeClockTime);
+            pw.print("=");
+            pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
+
+            pw.print("  mLastTimeChangeRealtime=");
+            pw.println(mLastTimeChangeRealtime);
+
+            pw.print("  mLastTickReceived=");
+            pw.println(sdf.format(new Date(mLastTickReceived)));
+
+            pw.print("  mLastTickSet=");
+            pw.println(sdf.format(new Date(mLastTickSet)));
 
             if (RECORD_ALARMS_IN_HISTORY) {
                 pw.println();
@@ -2258,16 +1965,23 @@
             long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
             long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
             pw.print("  Next non-wakeup alarm: ");
-                    TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
-                    pw.print(" = "); pw.print(mNextNonWakeup);
-                    pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
-            pw.print("    set at "); TimeUtils.formatDuration(mNextNonWakeUpSetAt, nowELAPSED, pw);
+            TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
+            pw.print(" = ");
+            pw.print(mNextNonWakeup);
+            pw.print(" = ");
+            pw.println(sdf.format(new Date(nextNonWakeupRTC)));
+            pw.print("    set at ");
+            TimeUtils.formatDuration(mNextNonWakeUpSetAt, nowELAPSED, pw);
             pw.println();
-            pw.print("  Next wakeup alarm: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
-                    pw.print(" = "); pw.print(mNextWakeup);
-                    pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
-            pw.print("    set at "); TimeUtils.formatDuration(mNextWakeUpSetAt, nowELAPSED, pw);
-                    pw.println();
+            pw.print("  Next wakeup alarm: ");
+            TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
+            pw.print(" = ");
+            pw.print(mNextWakeup);
+            pw.print(" = ");
+            pw.println(sdf.format(new Date(nextWakeupRTC)));
+            pw.print("    set at ");
+            TimeUtils.formatDuration(mNextWakeUpSetAt, nowELAPSED, pw);
+            pw.println();
 
             pw.print("  Next kernel non-wakeup alarm: ");
             TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME), pw);
@@ -2276,11 +1990,16 @@
             TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP), pw);
             pw.println();
 
-            pw.print("  Last wakeup: "); TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
-                    pw.print(" = "); pw.println(mLastWakeup);
-            pw.print("  Last trigger: "); TimeUtils.formatDuration(mLastTrigger, nowELAPSED, pw);
-                    pw.print(" = "); pw.println(mLastTrigger);
-            pw.print("  Num time change events: "); pw.println(mNumTimeChanged);
+            pw.print("  Last wakeup: ");
+            TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
+            pw.print(" = ");
+            pw.println(mLastWakeup);
+            pw.print("  Last trigger: ");
+            TimeUtils.formatDuration(mLastTrigger, nowELAPSED, pw);
+            pw.print(" = ");
+            pw.println(mLastTrigger);
+            pw.print("  Num time change events: ");
+            pw.println(mNumTimeChanged);
 
             pw.println();
             pw.println("  Next alarm clock information: ");
@@ -2295,23 +2014,24 @@
                 final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
                 final long time = next != null ? next.getTriggerTime() : 0;
                 final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
-                pw.print("    user:"); pw.print(user);
-                pw.print(" pendingSend:"); pw.print(pendingSend);
-                pw.print(" time:"); pw.print(time);
+                pw.print("    user:");
+                pw.print(user);
+                pw.print(" pendingSend:");
+                pw.print(pendingSend);
+                pw.print(" time:");
+                pw.print(time);
                 if (time > 0) {
-                    pw.print(" = "); pw.print(sdf.format(new Date(time)));
-                    pw.print(" = "); TimeUtils.formatDuration(time, nowRTC, pw);
+                    pw.print(" = ");
+                    pw.print(sdf.format(new Date(time)));
+                    pw.print(" = ");
+                    TimeUtils.formatDuration(time, nowRTC, pw);
                 }
                 pw.println();
             }
-            if (mAlarmBatches.size() > 0) {
+            if (mAlarmStore.size() > 0) {
                 pw.println();
-                pw.print("  Pending alarm batches: ");
-                pw.println(mAlarmBatches.size());
-                for (Batch b : mAlarmBatches) {
-                    pw.print(b); pw.println(':');
-                    dumpAlarmList(pw, b.alarms, "    ", nowELAPSED, nowRTC, sdf);
-                }
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", "  ");
+                mAlarmStore.dump(ipw, nowELAPSED, sdf);
             }
             pw.println();
             pw.println("  Pending user blocked background alarms: ");
@@ -2320,7 +2040,7 @@
                 final ArrayList<Alarm> blockedAlarms = mPendingBackgroundAlarms.valueAt(i);
                 if (blockedAlarms != null && blockedAlarms.size() > 0) {
                     blocked = true;
-                    dumpAlarmList(pw, blockedAlarms, "    ", nowELAPSED, nowRTC, sdf);
+                    dumpAlarmList(pw, blockedAlarms, "    ", nowELAPSED, sdf);
                 }
             }
             if (!blocked) {
@@ -2347,47 +2067,58 @@
                 pw.print("      Idling until: ");
                 if (mPendingIdleUntil != null) {
                     pw.println(mPendingIdleUntil);
-                    mPendingIdleUntil.dump(pw, "        ", nowELAPSED, nowRTC, sdf);
+                    mPendingIdleUntil.dump(pw, "        ", nowELAPSED, sdf);
                 } else {
                     pw.println("null");
                 }
                 pw.println("      Pending alarms:");
-                dumpAlarmList(pw, mPendingWhileIdleAlarms, "      ", nowELAPSED, nowRTC, sdf);
+                dumpAlarmList(pw, mPendingWhileIdleAlarms, "      ", nowELAPSED, sdf);
             }
             if (mNextWakeFromIdle != null) {
                 pw.println();
-                pw.print("  Next wake from idle: "); pw.println(mNextWakeFromIdle);
-                mNextWakeFromIdle.dump(pw, "    ", nowELAPSED, nowRTC, sdf);
+                pw.print("  Next wake from idle: ");
+                pw.println(mNextWakeFromIdle);
+                mNextWakeFromIdle.dump(pw, "    ", nowELAPSED, sdf);
             }
 
             pw.println();
             pw.print("  Past-due non-wakeup alarms: ");
             if (mPendingNonWakeupAlarms.size() > 0) {
                 pw.println(mPendingNonWakeupAlarms.size());
-                dumpAlarmList(pw, mPendingNonWakeupAlarms, "    ", nowELAPSED, nowRTC, sdf);
+                dumpAlarmList(pw, mPendingNonWakeupAlarms, "    ", nowELAPSED, sdf);
             } else {
                 pw.println("(none)");
             }
-            pw.print("    Number of delayed alarms: "); pw.print(mNumDelayedAlarms);
-            pw.print(", total delay time: "); TimeUtils.formatDuration(mTotalDelayTime, pw);
+            pw.print("    Number of delayed alarms: ");
+            pw.print(mNumDelayedAlarms);
+            pw.print(", total delay time: ");
+            TimeUtils.formatDuration(mTotalDelayTime, pw);
             pw.println();
-            pw.print("    Max delay time: "); TimeUtils.formatDuration(mMaxDelayTime, pw);
+            pw.print("    Max delay time: ");
+            TimeUtils.formatDuration(mMaxDelayTime, pw);
             pw.print(", max non-interactive time: ");
             TimeUtils.formatDuration(mNonInteractiveTime, pw);
             pw.println();
 
             pw.println();
-            pw.print("  Broadcast ref count: "); pw.println(mBroadcastRefCount);
-            pw.print("  PendingIntent send count: "); pw.println(mSendCount);
-            pw.print("  PendingIntent finish count: "); pw.println(mSendFinishCount);
-            pw.print("  Listener send count: "); pw.println(mListenerCount);
-            pw.print("  Listener finish count: "); pw.println(mListenerFinishCount);
+            pw.print("  Broadcast ref count: ");
+            pw.println(mBroadcastRefCount);
+            pw.print("  PendingIntent send count: ");
+            pw.println(mSendCount);
+            pw.print("  PendingIntent finish count: ");
+            pw.println(mSendFinishCount);
+            pw.print("  Listener send count: ");
+            pw.println(mListenerCount);
+            pw.print("  Listener finish count: ");
+            pw.println(mListenerFinishCount);
             pw.println();
 
             if (mInFlight.size() > 0) {
                 pw.println("Outstanding deliveries:");
                 for (int i = 0; i < mInFlight.size(); i++) {
-                    pw.print("   #"); pw.print(i); pw.print(": ");
+                    pw.print("   #");
+                    pw.print(i);
+                    pw.print(": ");
                     pw.println(mInFlight.get(i));
                 }
                 pw.println();
@@ -2395,7 +2126,7 @@
 
             if (mLastAllowWhileIdleDispatch.size() > 0) {
                 pw.println("  Last allow while idle dispatch times:");
-                for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) {
+                for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); i++) {
                     pw.print("    UID ");
                     final int uid = mLastAllowWhileIdleDispatch.keyAt(i);
                     UserHandle.formatUid(pw, uid);
@@ -2442,11 +2173,11 @@
             };
             int len = 0;
             // Get the top 10 FilterStats, ordered by aggregateTime.
-            for (int iu=0; iu<mBroadcastStats.size(); iu++) {
+            for (int iu = 0; iu < mBroadcastStats.size(); iu++) {
                 ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
-                for (int ip=0; ip<uidStats.size(); ip++) {
+                for (int ip = 0; ip < uidStats.size(); ip++) {
                     BroadcastStats bs = uidStats.valueAt(ip);
-                    for (int is=0; is<bs.filterStats.size(); is++) {
+                    for (int is = 0; is < bs.filterStats.size(); is++) {
                         FilterStats fs = bs.filterStats.valueAt(is);
                         int pos = len > 0
                                 ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0;
@@ -2456,7 +2187,7 @@
                         if (pos < topFilters.length) {
                             int copylen = topFilters.length - pos - 1;
                             if (copylen > 0) {
-                                System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
+                                System.arraycopy(topFilters, pos, topFilters, pos + 1, copylen);
                             }
                             topFilters[pos] = fs;
                             if (len < topFilters.length) {
@@ -2468,17 +2199,22 @@
             }
             if (len > 0) {
                 pw.println("  Top Alarms:");
-                for (int i=0; i<len; i++) {
+                for (int i = 0; i < len; i++) {
                     FilterStats fs = topFilters[i];
                     pw.print("    ");
                     if (fs.nesting > 0) pw.print("*ACTIVE* ");
                     TimeUtils.formatDuration(fs.aggregateTime, pw);
-                    pw.print(" running, "); pw.print(fs.numWakeup);
-                    pw.print(" wakeups, "); pw.print(fs.count);
-                    pw.print(" alarms: "); UserHandle.formatUid(pw, fs.mBroadcastStats.mUid);
-                    pw.print(":"); pw.print(fs.mBroadcastStats.mPackageName);
+                    pw.print(" running, ");
+                    pw.print(fs.numWakeup);
+                    pw.print(" wakeups, ");
+                    pw.print(fs.count);
+                    pw.print(" alarms: ");
+                    UserHandle.formatUid(pw, fs.mBroadcastStats.mUid);
+                    pw.print(":");
+                    pw.print(fs.mBroadcastStats.mPackageName);
                     pw.println();
-                    pw.print("      "); pw.print(fs.mTag);
+                    pw.print("      ");
+                    pw.print(fs.mTag);
                     pw.println();
                 }
             }
@@ -2486,36 +2222,40 @@
             pw.println(" ");
             pw.println("  Alarm Stats:");
             final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
-            for (int iu=0; iu<mBroadcastStats.size(); iu++) {
+            for (int iu = 0; iu < mBroadcastStats.size(); iu++) {
                 ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
-                for (int ip=0; ip<uidStats.size(); ip++) {
+                for (int ip = 0; ip < uidStats.size(); ip++) {
                     BroadcastStats bs = uidStats.valueAt(ip);
                     pw.print("  ");
                     if (bs.nesting > 0) pw.print("*ACTIVE* ");
                     UserHandle.formatUid(pw, bs.mUid);
                     pw.print(":");
                     pw.print(bs.mPackageName);
-                    pw.print(" "); TimeUtils.formatDuration(bs.aggregateTime, pw);
-                            pw.print(" running, "); pw.print(bs.numWakeup);
-                            pw.println(" wakeups:");
+                    pw.print(" ");
+                    TimeUtils.formatDuration(bs.aggregateTime, pw);
+                    pw.print(" running, ");
+                    pw.print(bs.numWakeup);
+                    pw.println(" wakeups:");
                     tmpFilters.clear();
-                    for (int is=0; is<bs.filterStats.size(); is++) {
+                    for (int is = 0; is < bs.filterStats.size(); is++) {
                         tmpFilters.add(bs.filterStats.valueAt(is));
                     }
                     Collections.sort(tmpFilters, comparator);
-                    for (int i=0; i<tmpFilters.size(); i++) {
+                    for (int i = 0; i < tmpFilters.size(); i++) {
                         FilterStats fs = tmpFilters.get(i);
                         pw.print("    ");
-                                if (fs.nesting > 0) pw.print("*ACTIVE* ");
-                                TimeUtils.formatDuration(fs.aggregateTime, pw);
-                                pw.print(" "); pw.print(fs.numWakeup);
-                                pw.print(" wakes " ); pw.print(fs.count);
-                                pw.print(" alarms, last ");
-                                TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
-                                pw.println(":");
+                        if (fs.nesting > 0) pw.print("*ACTIVE* ");
+                        TimeUtils.formatDuration(fs.aggregateTime, pw);
+                        pw.print(" ");
+                        pw.print(fs.numWakeup);
+                        pw.print(" wakes ");
+                        pw.print(fs.count);
+                        pw.print(" alarms, last ");
+                        TimeUtils.formatDuration(fs.lastTime, nowELAPSED, pw);
+                        pw.println(":");
                         pw.print("      ");
-                                pw.print(fs.mTag);
-                                pw.println();
+                        pw.print(fs.mTag);
+                        pw.println();
                     }
                 }
             }
@@ -2547,26 +2287,6 @@
                     }
                 }
             }
-
-            if (WAKEUP_STATS) {
-                pw.println();
-                pw.println("  Recent Wakeup History:");
-                long last = -1;
-                for (WakeupEvent event : mRecentWakeups) {
-                    pw.print("    "); pw.print(sdf.format(new Date(event.when)));
-                    pw.print('|');
-                    if (last < 0) {
-                        pw.print('0');
-                    } else {
-                        pw.print(event.when - last);
-                    }
-                    last = event.when;
-                    pw.print('|'); pw.print(event.uid);
-                    pw.print('|'); pw.print(event.action);
-                    pw.println();
-                }
-                pw.println();
-            }
         }
     }
 
@@ -2626,42 +2346,41 @@
                 final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
                 final long time = next != null ? next.getTriggerTime() : 0;
                 final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
-                final long aToken = proto.start(AlarmManagerServiceDumpProto.NEXT_ALARM_CLOCK_METADATA);
+                final long aToken = proto.start(
+                        AlarmManagerServiceDumpProto.NEXT_ALARM_CLOCK_METADATA);
                 proto.write(AlarmClockMetadataProto.USER, user);
                 proto.write(AlarmClockMetadataProto.IS_PENDING_SEND, pendingSend);
                 proto.write(AlarmClockMetadataProto.TRIGGER_TIME_MS, time);
                 proto.end(aToken);
             }
-            for (Batch b : mAlarmBatches) {
-                b.dumpDebug(proto, AlarmManagerServiceDumpProto.PENDING_ALARM_BATCHES,
-                        nowElapsed, nowRTC);
-            }
+            mAlarmStore.dumpProto(proto, nowElapsed);
+
             for (int i = 0; i < mPendingBackgroundAlarms.size(); i++) {
                 final ArrayList<Alarm> blockedAlarms = mPendingBackgroundAlarms.valueAt(i);
                 if (blockedAlarms != null) {
                     for (Alarm a : blockedAlarms) {
                         a.dumpDebug(proto,
                                 AlarmManagerServiceDumpProto.PENDING_USER_BLOCKED_BACKGROUND_ALARMS,
-                                nowElapsed, nowRTC);
+                                nowElapsed);
                     }
                 }
             }
             if (mPendingIdleUntil != null) {
                 mPendingIdleUntil.dumpDebug(
-                        proto, AlarmManagerServiceDumpProto.PENDING_IDLE_UNTIL, nowElapsed, nowRTC);
+                        proto, AlarmManagerServiceDumpProto.PENDING_IDLE_UNTIL, nowElapsed);
             }
             for (Alarm a : mPendingWhileIdleAlarms) {
                 a.dumpDebug(proto, AlarmManagerServiceDumpProto.PENDING_WHILE_IDLE_ALARMS,
-                        nowElapsed, nowRTC);
+                        nowElapsed);
             }
             if (mNextWakeFromIdle != null) {
                 mNextWakeFromIdle.dumpDebug(proto, AlarmManagerServiceDumpProto.NEXT_WAKE_FROM_IDLE,
-                        nowElapsed, nowRTC);
+                        nowElapsed);
             }
 
             for (Alarm a : mPendingNonWakeupAlarms) {
                 a.dumpDebug(proto, AlarmManagerServiceDumpProto.PAST_DUE_NON_WAKEUP_ALARMS,
-                        nowElapsed, nowRTC);
+                        nowElapsed);
             }
 
             proto.write(AlarmManagerServiceDumpProto.DELAYED_ALARM_COUNT, mNumDelayedAlarms);
@@ -2687,7 +2406,8 @@
                 final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
 
                 proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.UID, uid);
-                proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime);
+                proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.TIME_MS,
+                        lastTime);
                 proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
                         lastTime + getWhileIdleMinIntervalLocked(uid));
                 proto.end(token);
@@ -2730,7 +2450,7 @@
                         if (pos < topFilters.length) {
                             int copylen = topFilters.length - pos - 1;
                             if (copylen > 0) {
-                                System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
+                                System.arraycopy(topFilters, pos, topFilters, pos + 1, copylen);
                             }
                             topFilters[pos] = fs;
                             if (len < topFilters.length) {
@@ -2792,68 +2512,11 @@
                     proto.end(token);
                 }
             }
-
-            if (WAKEUP_STATS) {
-                for (WakeupEvent event : mRecentWakeups) {
-                    final long token = proto.start(AlarmManagerServiceDumpProto.RECENT_WAKEUP_HISTORY);
-                    proto.write(WakeupEventProto.UID, event.uid);
-                    proto.write(WakeupEventProto.ACTION, event.action);
-                    proto.write(WakeupEventProto.WHEN, event.when);
-                    proto.end(token);
-                }
-            }
         }
 
         proto.flush();
     }
 
-    private void logBatchesLocked(SimpleDateFormat sdf) {
-        ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
-        PrintWriter pw = new PrintWriter(bs);
-        final long nowRTC = mInjector.getCurrentTimeMillis();
-        final long nowELAPSED = mInjector.getElapsedRealtime();
-        final int NZ = mAlarmBatches.size();
-        for (int iz = 0; iz < NZ; iz++) {
-            Batch bz = mAlarmBatches.get(iz);
-            pw.append("Batch "); pw.print(iz); pw.append(": "); pw.println(bz);
-            dumpAlarmList(pw, bz.alarms, "  ", nowELAPSED, nowRTC, sdf);
-            pw.flush();
-            Slog.v(TAG, bs.toString());
-            bs.reset();
-        }
-    }
-
-    private boolean validateConsistencyLocked() {
-        if (DEBUG_VALIDATE) {
-            long lastTime = Long.MIN_VALUE;
-            final int N = mAlarmBatches.size();
-            for (int i = 0; i < N; i++) {
-                Batch b = mAlarmBatches.get(i);
-                if (b.start >= lastTime) {
-                    // duplicate start times are okay because of standalone batches
-                    lastTime = b.start;
-                } else {
-                    Slog.e(TAG, "CONSISTENCY FAILURE: Batch " + i + " is out of order");
-                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                    logBatchesLocked(sdf);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    private Batch findFirstWakeupBatchLocked() {
-        final int N = mAlarmBatches.size();
-        for (int i = 0; i < N; i++) {
-            Batch b = mAlarmBatches.get(i);
-            if (b.hasWakeups()) {
-                return b;
-            }
-        }
-        return null;
-    }
-
     long getNextWakeFromIdleTimeImpl() {
         synchronized (mLock) {
             return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
@@ -2881,41 +2544,34 @@
         }
         mNextAlarmClockMayChange = false;
 
-        SparseArray<AlarmManager.AlarmClockInfo> nextForUser = mTmpSparseAlarmClockArray;
+        final SparseArray<AlarmManager.AlarmClockInfo> nextForUser = mTmpSparseAlarmClockArray;
         nextForUser.clear();
 
-        final int N = mAlarmBatches.size();
-        for (int i = 0; i < N; i++) {
-            ArrayList<Alarm> alarms = mAlarmBatches.get(i).alarms;
-            final int M = alarms.size();
+        final ArrayList<Alarm> allAlarms = mAlarmStore.asList();
+        for (final Alarm a : allAlarms) {
+            if (a.alarmClock != null) {
+                final int userId = UserHandle.getUserId(a.uid);
+                final AlarmManager.AlarmClockInfo current = mNextAlarmClockForUser.get(userId);
 
-            for (int j = 0; j < M; j++) {
-                Alarm a = alarms.get(j);
-                if (a.alarmClock != null) {
-                    final int userId = UserHandle.getUserId(a.uid);
-                    AlarmManager.AlarmClockInfo current = mNextAlarmClockForUser.get(userId);
+                if (DEBUG_ALARM_CLOCK) {
+                    Log.v(TAG, "Found AlarmClockInfo " + a.alarmClock + " at "
+                            + formatNextAlarm(getContext(), a.alarmClock, userId)
+                            + " for user " + userId);
+                }
 
-                    if (DEBUG_ALARM_CLOCK) {
-                        Log.v(TAG, "Found AlarmClockInfo " + a.alarmClock + " at " +
-                                formatNextAlarm(getContext(), a.alarmClock, userId) +
-                                " for user " + userId);
-                    }
-
-                    // Alarms and batches are sorted by time, no need to compare times here.
-                    if (nextForUser.get(userId) == null) {
-                        nextForUser.put(userId, a.alarmClock);
-                    } else if (a.alarmClock.equals(current)
-                            && current.getTriggerTime() <= nextForUser.get(userId).getTriggerTime()) {
-                        // same/earlier time and it's the one we cited before, so stick with it
-                        nextForUser.put(userId, current);
-                    }
+                // AlarmClocks are sorted by time, so no need to compare times here.
+                if (nextForUser.get(userId) == null) {
+                    nextForUser.put(userId, a.alarmClock);
+                } else if (a.alarmClock.equals(current)
+                        && current.getTriggerTime() <= nextForUser.get(userId).getTriggerTime()) {
+                    // same/earlier time and it's the one we cited before, so stick with it
+                    nextForUser.put(userId, current);
                 }
             }
         }
 
-        // Update mNextAlarmForUser with new values.
-        final int NN = nextForUser.size();
-        for (int i = 0; i < NN; i++) {
+        final int newUserCount = nextForUser.size();
+        for (int i = 0; i < newUserCount; i++) {
             AlarmManager.AlarmClockInfo newAlarm = nextForUser.valueAt(i);
             int userId = nextForUser.keyAt(i);
             AlarmManager.AlarmClockInfo currentAlarm = mNextAlarmClockForUser.get(userId);
@@ -2924,9 +2580,8 @@
             }
         }
 
-        // Remove users without any alarm clocks scheduled.
-        final int NNN = mNextAlarmClockForUser.size();
-        for (int i = NNN - 1; i >= 0; i--) {
+        final int oldUserCount = mNextAlarmClockForUser.size();
+        for (int i = oldUserCount - 1; i >= 0; i--) {
             int userId = mNextAlarmClockForUser.keyAt(i);
             if (nextForUser.get(userId) == null) {
                 updateNextAlarmInfoForUserLocked(userId, null);
@@ -2967,16 +2622,16 @@
         pendingUsers.clear();
 
         synchronized (mLock) {
-            final int N  = mPendingSendNextAlarmClockChangedForUser.size();
-            for (int i = 0; i < N; i++) {
+            final int n = mPendingSendNextAlarmClockChangedForUser.size();
+            for (int i = 0; i < n; i++) {
                 int userId = mPendingSendNextAlarmClockChangedForUser.keyAt(i);
                 pendingUsers.append(userId, mNextAlarmClockForUser.get(userId));
             }
             mPendingSendNextAlarmClockChangedForUser.clear();
         }
 
-        final int N = pendingUsers.size();
-        for (int i = 0; i < N; i++) {
+        final int n = pendingUsers.size();
+        for (int i = 0; i < n; i++) {
             int userId = pendingUsers.keyAt(i);
             AlarmManager.AlarmClockInfo alarmClock = pendingUsers.valueAt(i);
             Settings.System.putStringForUser(getContext().getContentResolver(),
@@ -3005,16 +2660,16 @@
         // prior to that which contains no wakeups, we schedule that as well.
         final long nowElapsed = mInjector.getElapsedRealtime();
         long nextNonWakeup = 0;
-        if (mAlarmBatches.size() > 0) {
-            final Batch firstWakeup = findFirstWakeupBatchLocked();
-            final Batch firstBatch = mAlarmBatches.get(0);
-            if (firstWakeup != null) {
-                mNextWakeup = firstWakeup.start;
+        if (mAlarmStore.size() > 0) {
+            final long firstWakeup = mAlarmStore.getNextWakeupDeliveryTime();
+            final long first = mAlarmStore.getNextDeliveryTime();
+            if (firstWakeup != 0) {
+                mNextWakeup = firstWakeup;
                 mNextWakeUpSetAt = nowElapsed;
-                setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
+                setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup);
             }
-            if (firstBatch != firstWakeup) {
-                nextNonWakeup = firstBatch.start;
+            if (first != firstWakeup) {
+                nextNonWakeup = first;
             }
         }
         if (mPendingNonWakeupAlarms.size() > 0) {
@@ -3038,15 +2693,13 @@
             return;
         }
 
-        boolean didRemove = false;
         final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(operation, directReceiver);
-        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
-            Batch b = mAlarmBatches.get(i);
-            didRemove |= b.remove(whichAlarms, false);
-            if (b.size() == 0) {
-                mAlarmBatches.remove(i);
-            }
+        final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms);
+        for (final Alarm removed : removedAlarms) {
+            decrementAlarmCount(removed.uid, 1);
         }
+        final boolean didRemove = !removedAlarms.isEmpty();
+
         for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
             final Alarm alarm = mPendingWhileIdleAlarms.get(i);
             if (alarm.matches(operation, directReceiver)) {
@@ -3080,11 +2733,18 @@
             }
             if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) {
                 mNextWakeFromIdle = null;
+                mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+                    if (alarm != mPendingIdleUntil) {
+                        return false;
+                    }
+                    restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
+                    return adjustIdleUntilTime(alarm);
+                });
             }
-            rebatchAllAlarmsLocked(true);
             if (restorePending) {
                 restorePendingWhileIdleAlarmsLocked();
             }
+            rescheduleKernelAlarmsLocked();
             updateNextAlarmClockLocked();
         }
     }
@@ -3094,15 +2754,14 @@
             // If a force-stop occurs for a system-uid package, ignore it.
             return;
         }
-        boolean didRemove = false;
+
         final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid;
-        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
-            Batch b = mAlarmBatches.get(i);
-            didRemove |= b.remove(whichAlarms, false);
-            if (b.size() == 0) {
-                mAlarmBatches.remove(i);
-            }
+        final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
+        final boolean didRemove = !removed.isEmpty();
+        if (didRemove) {
+            decrementAlarmCount(uid, removed.size());
         }
+
         for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
             final Alarm a = mPendingWhileIdleAlarms.get(i);
             if (a.uid == uid) {
@@ -3111,7 +2770,7 @@
                 decrementAlarmCount(uid, 1);
             }
         }
-        for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) {
+        for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
             final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
             for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
                 if (alarmsForUid.get(j).uid == uid) {
@@ -3127,17 +2786,24 @@
         // make sure to reset to other triggers.
         if (mNextWakeFromIdle != null && mNextWakeFromIdle.uid == uid) {
             mNextWakeFromIdle = null;
+            mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+                if (alarm != mPendingIdleUntil) {
+                    return false;
+                }
+                restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
+                return adjustIdleUntilTime(alarm);
+            });
         }
         if (mPendingIdleUntil != null && mPendingIdleUntil.uid == uid) {
             // Should never happen - only the system uid is allowed to set idle-until alarms
             Slog.wtf(TAG, "Removed app uid " + uid + " set idle-until alarm!");
             mPendingIdleUntil = null;
+            restorePendingWhileIdleAlarmsLocked();
         }
         if (didRemove) {
             if (DEBUG_BATCH) {
                 Slog.v(TAG, "remove(uid) changed bounds; rebatching");
             }
-            rebatchAllAlarmsLocked(true);
             rescheduleKernelAlarmsLocked();
             updateNextAlarmClockLocked();
         }
@@ -3152,7 +2818,6 @@
             return;
         }
 
-        boolean didRemove = false;
         final MutableBoolean removedNextWakeFromIdle = new MutableBoolean(false);
         final Predicate<Alarm> whichAlarms = (Alarm a) -> {
             final boolean didMatch = a.matches(packageName);
@@ -3161,17 +2826,11 @@
             }
             return didMatch;
         };
-        final boolean oldHasTick = haveBatchesTimeTickAlarm(mAlarmBatches);
-        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
-            Batch b = mAlarmBatches.get(i);
-            didRemove |= b.remove(whichAlarms, false);
-            if (b.size() == 0) {
-                mAlarmBatches.remove(i);
-            }
-        }
-        final boolean newHasTick = haveBatchesTimeTickAlarm(mAlarmBatches);
-        if (oldHasTick != newHasTick) {
-            Slog.wtf(TAG, "removeLocked: hasTick changed from " + oldHasTick + " to " + newHasTick);
+
+        final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
+        final boolean didRemove = !removed.isEmpty();
+        if (didRemove) {
+            decrementAlarmCount(removed.get(0).uid, removed.size());
         }
 
         for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
@@ -3182,7 +2841,7 @@
                 decrementAlarmCount(a.uid, 1);
             }
         }
-        for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) {
+        for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
             final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
             for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
                 final Alarm alarm = alarmsForUid.get(j);
@@ -3199,12 +2858,18 @@
         // make sure to reset to other triggers.
         if (removedNextWakeFromIdle.value) {
             mNextWakeFromIdle = null;
+            mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+                if (alarm != mPendingIdleUntil) {
+                    return false;
+                }
+                restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
+                return adjustIdleUntilTime(alarm);
+            });
         }
         if (didRemove) {
             if (DEBUG_BATCH) {
                 Slog.v(TAG, "remove(package) changed bounds; rebatching");
             }
-            rebatchAllAlarmsLocked(true);
             rescheduleKernelAlarmsLocked();
             updateNextAlarmClockLocked();
         }
@@ -3216,16 +2881,14 @@
             // If a force-stop occurs for a system-uid package, ignore it.
             return;
         }
-        boolean didRemove = false;
         final Predicate<Alarm> whichAlarms = (a) -> (a.uid == uid
                 && mActivityManagerInternal.isAppStartModeDisabled(uid, a.packageName));
-        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
-            Batch b = mAlarmBatches.get(i);
-            didRemove |= b.remove(whichAlarms, false);
-            if (b.size() == 0) {
-                mAlarmBatches.remove(i);
-            }
+        final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms);
+        final boolean didRemove = !removed.isEmpty();
+        if (didRemove) {
+            decrementAlarmCount(uid, removed.size());
         }
+
         for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
             final Alarm a = mPendingWhileIdleAlarms.get(i);
             if (a.uid == uid) {
@@ -3247,7 +2910,6 @@
             if (DEBUG_BATCH) {
                 Slog.v(TAG, "remove(package) changed bounds; rebatching");
             }
-            rebatchAllAlarmsLocked(true);
             rescheduleKernelAlarmsLocked();
             updateNextAlarmClockLocked();
         }
@@ -3258,16 +2920,14 @@
             // If we're told we're removing the system user, ignore it.
             return;
         }
-        boolean didRemove = false;
         final Predicate<Alarm> whichAlarms =
                 (Alarm a) -> UserHandle.getUserId(a.creatorUid) == userHandle;
-        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
-            Batch b = mAlarmBatches.get(i);
-            didRemove |= b.remove(whichAlarms, false);
-            if (b.size() == 0) {
-                mAlarmBatches.remove(i);
-            }
+        final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms);
+        for (final Alarm removed : removedAlarms) {
+            decrementAlarmCount(removed.uid, 1);
         }
+        final boolean didRemove = !removedAlarms.isEmpty();
+
         for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
             if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).creatorUid)
                     == userHandle) {
@@ -3297,7 +2957,6 @@
             if (DEBUG_BATCH) {
                 Slog.v(TAG, "remove(user) changed bounds; rebatching");
             }
-            rebatchAllAlarmsLocked(true);
             rescheduleKernelAlarmsLocked();
             updateNextAlarmClockLocked();
         }
@@ -3333,9 +2992,9 @@
     }
 
     boolean lookForPackageLocked(String packageName) {
-        for (int i = 0; i < mAlarmBatches.size(); i++) {
-            Batch b = mAlarmBatches.get(i);
-            if (b.hasPackage(packageName)) {
+        final ArrayList<Alarm> allAlarms = mAlarmStore.asList();
+        for (final Alarm alarm : allAlarms) {
+            if (alarm.matches(packageName)) {
                 return true;
             }
         }
@@ -3360,34 +3019,39 @@
         }
     }
 
-    private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
-            String prefix, String label, long nowELAPSED, long nowRTC, SimpleDateFormat sdf) {
-        for (int i=list.size()-1; i>=0; i--) {
-            Alarm a = list.get(i);
-            pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
-                    pw.print(": "); pw.println(a);
-            a.dump(pw, prefix + "  ", nowELAPSED, nowRTC, sdf);
-        }
-    }
-
     private static final String labelForType(int type) {
         switch (type) {
-        case RTC: return "RTC";
-        case RTC_WAKEUP : return "RTC_WAKEUP";
-        case ELAPSED_REALTIME : return "ELAPSED";
-        case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP";
+            case RTC:
+                return "RTC";
+            case RTC_WAKEUP:
+                return "RTC_WAKEUP";
+            case ELAPSED_REALTIME:
+                return "ELAPSED";
+            case ELAPSED_REALTIME_WAKEUP:
+                return "ELAPSED_WAKEUP";
         }
         return "--unknown--";
     }
 
     private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
-            String prefix, long nowELAPSED, long nowRTC, SimpleDateFormat sdf) {
-        for (int i=list.size()-1; i>=0; i--) {
-            Alarm a = list.get(i);
+            String prefix, long nowELAPSED, SimpleDateFormat sdf) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix);
+        dumpAlarmList(ipw, list, nowELAPSED, sdf);
+    }
+
+    static final void dumpAlarmList(IndentingPrintWriter ipw, ArrayList<Alarm> list,
+            long nowELAPSED, SimpleDateFormat sdf) {
+        for (int i = list.size() - 1; i >= 0; i--) {
+            final Alarm a = list.get(i);
             final String label = labelForType(a.type);
-            pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
-                    pw.print(": "); pw.println(a);
-            a.dump(pw, prefix + "  ", nowELAPSED, nowRTC, sdf);
+            ipw.print(label);
+            ipw.print(" #");
+            ipw.print(i);
+            ipw.print(": ");
+            ipw.println(a);
+            ipw.increaseIndent();
+            a.dump(ipw, nowELAPSED, sdf);
+            ipw.decreaseIndent();
         }
     }
 
@@ -3442,105 +3106,93 @@
 
     boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED) {
         boolean hasWakeup = false;
-        // batches are temporally sorted, so we need only pull from the
-        // start of the list until we either empty it or hit a batch
-        // that is not yet deliverable
-        while (mAlarmBatches.size() > 0) {
-            Batch batch = mAlarmBatches.get(0);
-            if (batch.start > nowELAPSED) {
-                // Everything else is scheduled for the future
-                break;
-            }
-
-            // We will (re)schedule some alarms now; don't let that interfere
-            // with delivery of this current batch
-            mAlarmBatches.remove(0);
-
-            final int N = batch.size();
-            for (int i = 0; i < N; i++) {
-                Alarm alarm = batch.get(i);
-
-                if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
-                    // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
-                    // schedule such alarms.  The first such alarm from an app is always delivered.
-                    final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, -1);
-                    final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid);
-                    if (lastTime >= 0 && nowELAPSED < minTime) {
-                        // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
-                        // alarm went off for this app.  Reschedule the alarm to be in the
-                        // correct time period.
-                        alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
-                        if (alarm.maxWhenElapsed < minTime) {
-                            alarm.maxWhenElapsed = minTime;
-                        }
-                        alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
-                        if (RECORD_DEVICE_IDLE_ALARMS) {
-                            IdleDispatchEntry ent = new IdleDispatchEntry();
-                            ent.uid = alarm.uid;
-                            ent.pkg = alarm.operation.getCreatorPackage();
-                            ent.tag = alarm.operation.getTag("");
-                            ent.op = "RESCHEDULE";
-                            ent.elapsedRealtime = nowELAPSED;
-                            ent.argRealtime = lastTime;
-                            mAllowWhileIdleDispatches.add(ent);
-                        }
-                        setImplLocked(alarm, true, false);
-                        continue;
+        final ArrayList<Alarm> pendingAlarms = mAlarmStore.removePendingAlarms(nowELAPSED);
+        for (final Alarm alarm : pendingAlarms) {
+            if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+                // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
+                // schedule such alarms.  The first such alarm from an app is always delivered.
+                final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, -1);
+                final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid);
+                if (lastTime >= 0 && nowELAPSED < minTime) {
+                    // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
+                    // alarm went off for this app.  Reschedule the alarm to be in the
+                    // correct time period.
+                    alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
+                    if (alarm.maxWhenElapsed < minTime) {
+                        alarm.maxWhenElapsed = minTime;
                     }
-                }
-                if (isBackgroundRestricted(alarm)) {
-                    // Alarms with FLAG_WAKE_FROM_IDLE or mPendingIdleUntil alarm are not deferred
-                    if (DEBUG_BG_LIMIT) {
-                        Slog.d(TAG, "Deferring alarm " + alarm + " due to user forced app standby");
+                    alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
+                    if (RECORD_DEVICE_IDLE_ALARMS) {
+                        IdleDispatchEntry ent = new IdleDispatchEntry();
+                        ent.uid = alarm.uid;
+                        ent.pkg = alarm.operation.getCreatorPackage();
+                        ent.tag = alarm.operation.getTag("");
+                        ent.op = "RESCHEDULE";
+                        ent.elapsedRealtime = nowELAPSED;
+                        ent.argRealtime = lastTime;
+                        mAllowWhileIdleDispatches.add(ent);
                     }
-                    ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.get(alarm.creatorUid);
-                    if (alarmsForUid == null) {
-                        alarmsForUid = new ArrayList<>();
-                        mPendingBackgroundAlarms.put(alarm.creatorUid, alarmsForUid);
-                    }
-                    alarmsForUid.add(alarm);
+                    setImplLocked(alarm);
                     continue;
                 }
+            }
+            if (isBackgroundRestricted(alarm)) {
+                // Alarms with FLAG_WAKE_FROM_IDLE or mPendingIdleUntil alarm are not deferred
+                if (DEBUG_BG_LIMIT) {
+                    Slog.d(TAG, "Deferring alarm " + alarm + " due to user forced app standby");
+                }
+                ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.get(alarm.creatorUid);
+                if (alarmsForUid == null) {
+                    alarmsForUid = new ArrayList<>();
+                    mPendingBackgroundAlarms.put(alarm.creatorUid, alarmsForUid);
+                }
+                alarmsForUid.add(alarm);
+                continue;
+            }
 
-                alarm.count = 1;
-                triggerList.add(alarm);
-                if ((alarm.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
-                    EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
-                            alarm.statsTag);
-                }
-                if (mPendingIdleUntil == alarm) {
-                    mPendingIdleUntil = null;
-                    rebatchAllAlarmsLocked(false);
-                    restorePendingWhileIdleAlarmsLocked();
-                }
-                if (mNextWakeFromIdle == alarm) {
-                    mNextWakeFromIdle = null;
-                    rebatchAllAlarmsLocked(false);
-                }
+            alarm.count = 1;
+            triggerList.add(alarm);
+            if ((alarm.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+                EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0,
+                        alarm.statsTag);
+            }
+            if (mPendingIdleUntil == alarm) {
+                mPendingIdleUntil = null;
+                restorePendingWhileIdleAlarmsLocked();
+            }
+            if (mNextWakeFromIdle == alarm) {
+                mNextWakeFromIdle = null;
+                mAlarmStore.recalculateAlarmDeliveries(a -> {
+                    if (a != mPendingIdleUntil) {
+                        return false;
+                    }
+                    restoreAlarmLocked(a, nowELAPSED);
+                    return adjustIdleUntilTime(a);
+                });
+            }
 
-                // Recurring alarms may have passed several alarm intervals while the
-                // phone was asleep or off, so pass a trigger count when sending them.
-                if (alarm.repeatInterval > 0) {
-                    // this adjustment will be zero if we're late by
-                    // less than one full repeat interval
-                    alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
-                    // Also schedule its next recurrence
-                    final long delta = alarm.count * alarm.repeatInterval;
-                    final long nextElapsed = alarm.expectedWhenElapsed + delta;
-                    setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
-                            maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
-                            alarm.repeatInterval, alarm.operation, null, null, alarm.flags, true,
-                            alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
-                }
+            // Recurring alarms may have passed several alarm intervals while the
+            // phone was asleep or off, so pass a trigger count when sending them.
+            if (alarm.repeatInterval > 0) {
+                // this adjustment will be zero if we're late by
+                // less than one full repeat interval
+                alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
+                // Also schedule its next recurrence
+                final long delta = alarm.count * alarm.repeatInterval;
+                final long nextElapsed = alarm.expectedWhenElapsed + delta;
+                setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
+                        maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
+                        alarm.repeatInterval, alarm.operation, null, null, alarm.flags,
+                        alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
+            }
 
-                if (alarm.wakeup) {
-                    hasWakeup = true;
-                }
+            if (alarm.wakeup) {
+                hasWakeup = true;
+            }
 
-                // We removed an alarm clock. Let the caller recompute the next alarm clock.
-                if (alarm.alarmClock != null) {
-                    mNextAlarmClockMayChange = true;
-                }
+            // We removed an alarm clock. Let the caller recompute the next alarm clock.
+            if (alarm.alarmClock != null) {
+                mNextAlarmClockMayChange = true;
             }
         }
 
@@ -3551,7 +3203,7 @@
         Collections.sort(triggerList, mAlarmDispatchComparator);
 
         if (localLOGV) {
-            for (int i=0; i<triggerList.size(); i++) {
+            for (int i = 0; i < triggerList.size(); i++) {
                 Slog.v(TAG, "Triggering alarm #" + i + ": " + triggerList.get(i));
             }
         }
@@ -3559,218 +3211,31 @@
         return hasWakeup;
     }
 
-    /**
-     * This Comparator sorts Alarms into increasing time order.
-     */
-    public static class IncreasingTimeOrder implements Comparator<Alarm> {
-        public int compare(Alarm a1, Alarm a2) {
-            long when1 = a1.whenElapsed;
-            long when2 = a2.whenElapsed;
-            if (when1 > when2) {
-                return 1;
-            }
-            if (when1 < when2) {
-                return -1;
-            }
-            return 0;
-        }
-    }
-
-    @VisibleForTesting
-    static class Alarm {
-        public final int type;
-        public final long origWhen;
-        public final boolean wakeup;
-        public final PendingIntent operation;
-        public final IAlarmListener listener;
-        public final String listenerTag;
-        public final String statsTag;
-        public final WorkSource workSource;
-        public final int flags;
-        public final AlarmManager.AlarmClockInfo alarmClock;
-        public final int uid;
-        public final int creatorUid;
-        public final String packageName;
-        public final String sourcePackage;
-        public int count;
-        public long when;
-        public long windowLength;
-        public long whenElapsed;    // 'when' in the elapsed time base
-        public long maxWhenElapsed; // also in the elapsed time base
-        // Expected alarm expiry time before app standby deferring is applied.
-        public long expectedWhenElapsed;
-        public long expectedMaxWhenElapsed;
-        public long repeatInterval;
-        public PriorityClass priorityClass;
-
-        public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
-                long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag,
-                WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info,
-                int _uid, String _pkgName) {
-            type = _type;
-            origWhen = _when;
-            wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
-                    || _type == AlarmManager.RTC_WAKEUP;
-            when = _when;
-            whenElapsed = _whenElapsed;
-            expectedWhenElapsed = _whenElapsed;
-            windowLength = _windowLength;
-            maxWhenElapsed = expectedMaxWhenElapsed = clampPositive(_maxWhen);
-            repeatInterval = _interval;
-            operation = _op;
-            listener = _rec;
-            listenerTag = _listenerTag;
-            statsTag = makeTag(_op, _listenerTag, _type);
-            workSource = _ws;
-            flags = _flags;
-            alarmClock = _info;
-            uid = _uid;
-            packageName = _pkgName;
-            sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
-            creatorUid = (operation != null) ? operation.getCreatorUid() : uid;
-        }
-
-        public static String makeTag(PendingIntent pi, String tag, int type) {
-            final String alarmString = type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
-                    ? "*walarm*:" : "*alarm*:";
-            return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag);
-        }
-
-        public WakeupEvent makeWakeupEvent(long nowRTC) {
-            return new WakeupEvent(nowRTC, creatorUid,
-                    (operation != null)
-                        ? operation.getIntent().getAction()
-                        : ("<listener>:" + listenerTag));
-        }
-
-        // Returns true if either matches
-        public boolean matches(PendingIntent pi, IAlarmListener rec) {
-            return (operation != null)
-                    ? operation.equals(pi)
-                    : rec != null && listener.asBinder().equals(rec.asBinder());
-        }
-
-        public boolean matches(String packageName) {
-            return packageName.equals(sourcePackage);
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder(128);
-            sb.append("Alarm{");
-            sb.append(Integer.toHexString(System.identityHashCode(this)));
-            sb.append(" type ");
-            sb.append(type);
-            sb.append(" when ");
-            sb.append(when);
-            sb.append(" ");
-            sb.append(sourcePackage);
-            sb.append('}');
-            return sb.toString();
-        }
-
-        public void dump(PrintWriter pw, String prefix, long nowELAPSED, long nowRTC,
-                SimpleDateFormat sdf) {
-            final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
-            pw.print(prefix); pw.print("tag="); pw.println(statsTag);
-            pw.print(prefix); pw.print("type="); pw.print(type);
-                    pw.print(" expectedWhenElapsed="); TimeUtils.formatDuration(
-                    expectedWhenElapsed, nowELAPSED, pw);
-                    pw.print(" expectedMaxWhenElapsed="); TimeUtils.formatDuration(
-                    expectedMaxWhenElapsed, nowELAPSED, pw);
-                    pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
-                            nowELAPSED, pw);
-                    pw.print(" maxWhenElapsed="); TimeUtils.formatDuration(maxWhenElapsed,
-                            nowELAPSED, pw);
-                    pw.print(" when=");
-                    if (isRtc) {
-                        pw.print(sdf.format(new Date(when)));
-                    } else {
-                        TimeUtils.formatDuration(when, nowELAPSED, pw);
-                    }
-                    pw.println();
-            pw.print(prefix); pw.print("window="); TimeUtils.formatDuration(windowLength, pw);
-                    pw.print(" repeatInterval="); pw.print(repeatInterval);
-                    pw.print(" count="); pw.print(count);
-                    pw.print(" flags=0x"); pw.println(Integer.toHexString(flags));
-            if (alarmClock != null) {
-                pw.print(prefix); pw.println("Alarm clock:");
-                pw.print(prefix); pw.print("  triggerTime=");
-                pw.println(sdf.format(new Date(alarmClock.getTriggerTime())));
-                pw.print(prefix); pw.print("  showIntent="); pw.println(alarmClock.getShowIntent());
-            }
-            pw.print(prefix); pw.print("operation="); pw.println(operation);
-            if (listener != null) {
-                pw.print(prefix); pw.print("listener="); pw.println(listener.asBinder());
-            }
-        }
-
-        public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed,
-                long nowRTC) {
-            final long token = proto.start(fieldId);
-
-            proto.write(AlarmProto.TAG, statsTag);
-            proto.write(AlarmProto.TYPE, type);
-            proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
-            proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
-            proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
-            proto.write(AlarmProto.COUNT, count);
-            proto.write(AlarmProto.FLAGS, flags);
-            if (alarmClock != null) {
-                alarmClock.dumpDebug(proto, AlarmProto.ALARM_CLOCK);
-            }
-            if (operation != null) {
-                operation.dumpDebug(proto, AlarmProto.OPERATION);
-            }
-            if (listener != null) {
-                proto.write(AlarmProto.LISTENER, listener.asBinder().toString());
-            }
-
-            proto.end(token);
-        }
-    }
-
-    void recordWakeupAlarms(ArrayList<Batch> batches, long nowELAPSED, long nowRTC) {
-        final int numBatches = batches.size();
-        for (int nextBatch = 0; nextBatch < numBatches; nextBatch++) {
-            Batch b = batches.get(nextBatch);
-            if (b.start > nowELAPSED) {
-                break;
-            }
-
-            final int numAlarms = b.alarms.size();
-            for (int nextAlarm = 0; nextAlarm < numAlarms; nextAlarm++) {
-                Alarm a = b.alarms.get(nextAlarm);
-                mRecentWakeups.add(a.makeWakeupEvent(nowRTC));
-            }
-        }
-    }
-
     long currentNonWakeupFuzzLocked(long nowELAPSED) {
         long timeSinceOn = nowELAPSED - mNonInteractiveStartTime;
-        if (timeSinceOn < 5*60*1000) {
+        if (timeSinceOn < 5 * 60 * 1000) {
             // If the screen has been off for 5 minutes, only delay by at most two minutes.
-            return 2*60*1000;
-        } else if (timeSinceOn < 30*60*1000) {
+            return 2 * 60 * 1000;
+        } else if (timeSinceOn < 30 * 60 * 1000) {
             // If the screen has been off for 30 minutes, only delay by at most 15 minutes.
-            return 15*60*1000;
+            return 15 * 60 * 1000;
         } else {
             // Otherwise, we will delay by at most an hour.
-            return 60*60*1000;
+            return 60 * 60 * 1000;
         }
     }
 
     static int fuzzForDuration(long duration) {
-        if (duration < 15*60*1000) {
+        if (duration < 15 * 60 * 1000) {
             // If the duration until the time is less than 15 minutes, the maximum fuzz
             // is the duration.
-            return (int)duration;
-        } else if (duration < 90*60*1000) {
+            return (int) duration;
+        } else if (duration < 90 * 60 * 1000) {
             // If duration is less than 1 1/2 hours, the maximum fuzz is 15 minutes,
-            return 15*60*1000;
+            return 15 * 60 * 1000;
         } else {
             // Otherwise, we will fuzz by at most half an hour.
-            return 30*60*1000;
+            return 30 * 60 * 1000;
         }
     }
 
@@ -3793,13 +3258,15 @@
 
     void deliverAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED) {
         mLastAlarmDeliveryTime = nowELAPSED;
-        for (int i=0; i<triggerList.size(); i++) {
+        for (int i = 0; i < triggerList.size(); i++) {
             Alarm alarm = triggerList.get(i);
-            final boolean allowWhileIdle = (alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
+            final boolean allowWhileIdle = (alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
             if (alarm.wakeup) {
-              Trace.traceBegin(Trace.TRACE_TAG_POWER, "Dispatch wakeup alarm to " + alarm.packageName);
+                Trace.traceBegin(Trace.TRACE_TAG_POWER,
+                        "Dispatch wakeup alarm to " + alarm.packageName);
             } else {
-              Trace.traceBegin(Trace.TRACE_TAG_POWER, "Dispatch non-wakeup alarm to " + alarm.packageName);
+                Trace.traceBegin(Trace.TRACE_TAG_POWER,
+                        "Dispatch non-wakeup alarm to " + alarm.packageName);
             }
             try {
                 if (localLOGV) {
@@ -3909,23 +3376,20 @@
         }
     }
 
-    private class AlarmThread extends Thread
-    {
+    private class AlarmThread extends Thread {
         private int mFalseWakeups;
         private int mWtfThreshold;
-        public AlarmThread()
-        {
+
+        AlarmThread() {
             super("AlarmManager");
             mFalseWakeups = 0;
             mWtfThreshold = 100;
         }
 
-        public void run()
-        {
+        public void run() {
             ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
 
-            while (true)
-            {
+            while (true) {
                 int result = mInjector.waitForAlarm();
                 final long nowRTC = mInjector.getCurrentTimeMillis();
                 final long nowELAPSED = mInjector.getElapsedRealtime();
@@ -3948,8 +3412,8 @@
                         expectedClockTime = lastTimeChangeClockTime
                                 + (nowELAPSED - mLastTimeChangeRealtime);
                     }
-                    if (lastTimeChangeClockTime == 0 || nowRTC < (expectedClockTime-1000)
-                            || nowRTC > (expectedClockTime+1000)) {
+                    if (lastTimeChangeClockTime == 0 || nowRTC < (expectedClockTime - 1000)
+                            || nowRTC > (expectedClockTime + 1000)) {
                         // The change is by at least +/- 1000 ms (or this is the first change),
                         // let's do it!
                         if (DEBUG_BATCH) {
@@ -3959,7 +3423,7 @@
                         FrameworkStatsLog.write(FrameworkStatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
                         removeImpl(null, mTimeTickTrigger);
                         removeImpl(mDateChangeSender, null);
-                        rebatchAllAlarms();
+                        reevaluateRtcAlarms(nowELAPSED);
                         mClockReceiver.scheduleTimeTickEvent();
                         mClockReceiver.scheduleDateChangedEvent();
                         synchronized (mLock) {
@@ -3984,24 +3448,9 @@
                     // If this was anything besides just a time change, then figure what if
                     // anything to do about alarms.
                     synchronized (mLock) {
-                        if (localLOGV) Slog.v(
-                            TAG, "Checking for alarms... rtc=" + nowRTC
-                            + ", elapsed=" + nowELAPSED);
-
-                        if (WAKEUP_STATS) {
-                            if ((result & IS_WAKEUP_MASK) != 0) {
-                                long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;
-                                int n = 0;
-                                for (WakeupEvent event : mRecentWakeups) {
-                                    if (event.when > newEarliest) break;
-                                    n++; // number of now-stale entries at the list head
-                                }
-                                for (int i = 0; i < n; i++) {
-                                    mRecentWakeups.remove();
-                                }
-
-                                recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);
-                            }
+                        if (localLOGV) {
+                            Slog.v(TAG, "Checking for alarms... rtc=" + nowRTC
+                                            + ", elapsed=" + nowELAPSED);
                         }
 
                         mLastTrigger = nowELAPSED;
@@ -4012,7 +3461,7 @@
                             if (mPendingNonWakeupAlarms.size() == 0) {
                                 mStartCurrentDelayTime = nowELAPSED;
                                 mNextNonWakeupDeliveryTime = nowELAPSED
-                                        + ((currentNonWakeupFuzzLocked(nowELAPSED)*3)/2);
+                                        + ((currentNonWakeupFuzzLocked(nowELAPSED) * 3) / 2);
                             }
                             mPendingNonWakeupAlarms.addAll(triggerList);
                             mNumDelayedAlarms += triggerList.size();
@@ -4074,7 +3523,8 @@
 
     /**
      * Attribute blame for a WakeLock.
-     * @param ws WorkSource to attribute blame.
+     *
+     * @param ws       WorkSource to attribute blame.
      * @param knownUid attribution uid; < 0 values are ignored.
      */
     void setWakelockWorkSource(WorkSource ws, int knownUid, String tag, boolean first) {
@@ -4135,7 +3585,7 @@
                     }
 
                     // now trigger the alarms without the lock held
-                    for (int i=0; i<triggerList.size(); i++) {
+                    for (int i = 0; i < triggerList.size(); i++) {
                         Alarm alarm = triggerList.get(i);
                         try {
                             alarm.operation.send();
@@ -4262,7 +3712,7 @@
 
             final WorkSource workSource = null; // Let system take blame for time tick events.
             setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
-                    0, null, mTimeTickTrigger, "TIME_TICK", AlarmManager.FLAG_STANDALONE,
+                    0, null, mTimeTickTrigger, TIME_TICK_TAG, AlarmManager.FLAG_STANDALONE,
                     workSource, null, Process.myUid(), "android");
 
             // Finally, remember when we set the tick alarm
@@ -4312,7 +3762,7 @@
             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
             filter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
             getContext().registerReceiver(this, filter);
-             // Register for events related to sdcard installation.
+            // Register for events related to sdcard installation.
             IntentFilter sdFilter = new IntentFilter();
             sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
             sdFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -4378,7 +3828,7 @@
                             removeLocked(pkg);
                         }
                         mPriorities.remove(pkg);
-                        for (int i=mBroadcastStats.size()-1; i>=0; i--) {
+                        for (int i = mBroadcastStats.size() - 1; i >= 0; i--) {
                             ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
                             if (uidStats.remove(pkg) != null) {
                                 if (uidStats.size() <= 0) {
@@ -4545,7 +3995,7 @@
                 if (mInFlight.size() > 0) {
                     mLog.w("Finished all dispatches with " + mInFlight.size()
                             + " remaining inflights");
-                    for (int i=0; i<mInFlight.size(); i++) {
+                    for (int i = 0; i < mInFlight.size(); i++) {
                         mLog.w("  Remaining #" + i + ": " + mInFlight.get(i));
                     }
                     mInFlight.clear();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
new file mode 100644
index 0000000..9fdbb8b
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 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.alarm;
+
+import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.FileDescriptor;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+/**
+ * Used by {@link AlarmManagerService} to store alarms.
+ * Besides basic add and remove operations, supports querying the next upcoming alarm times,
+ * and all the alarms that are due at a given time.
+ */
+public interface AlarmStore {
+
+    /**
+     * Adds the given alarm.
+     *
+     * @param a The alarm to add.
+     */
+    void add(Alarm a);
+
+    /**
+     * Removes alarms that pass the given predicate.
+     *
+     * @param whichAlarms The predicate describing the alarms to remove.
+     * @return a list containing alarms that were removed.
+     */
+    ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms);
+
+    /**
+     * Returns the total number of alarms in this store.
+     */
+    int size();
+
+    /**
+     * Get the next wakeup delivery time of all alarms in this store.
+     *
+     * @return a long timestamp in the {@link SystemClock#elapsedRealtime() elapsed}
+     * timebase.
+     */
+    long getNextWakeupDeliveryTime();
+
+    /**
+     * Get the next delivery time of all alarms in this store.
+     *
+     * @return a long timestamp in the {@link SystemClock#elapsedRealtime() elapsed}
+     * timebase. May or may not be the same as {{@link #getNextWakeupDeliveryTime()}}.
+     */
+    long getNextDeliveryTime();
+
+    /**
+     * Removes all alarms that are pending delivery at the given time.
+     *
+     * @param nowElapsed    The time at which delivery eligibility is evaluated.
+     * @return The list of alarms pending at the given time.
+     */
+    ArrayList<Alarm> removePendingAlarms(long nowElapsed);
+
+    /**
+     * Adjusts alarm deliveries for all alarms according to the passed
+     * {@link AlarmDeliveryCalculator}
+     *
+     * @return {@code true} if any of the alarm deliveries changed due to this call.
+     */
+    boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator);
+
+    /**
+     * Returns all the alarms in the form of a list.
+     */
+    ArrayList<Alarm> asList();
+
+    /**
+     * Dumps the state of this alarm store into the passed print writer. Also accepts the current
+     * timestamp and a {@link SimpleDateFormat} to format the timestamps as human readable delta
+     * from the current time.
+     *
+     * Primary useful for debugging. Can be called from the
+     * {@link android.os.Binder#dump(FileDescriptor PrintWriter, String[]) dump} method of the
+     * caller.
+     * @param ipw        The {@link IndentingPrintWriter} to write to.
+     * @param nowElapsed the time when the dump is requested in the
+     *                   {@link SystemClock#elapsedRealtime()
+     *                   elapsed} timebase.
+     * @param sdf        the date format to print timestamps in.
+     */
+    void dump(IndentingPrintWriter ipw, long nowElapsed, SimpleDateFormat sdf);
+
+    /**
+     * Dump the state of this alarm store as a proto buffer to the given stream.
+     */
+    void dumpProto(ProtoOutputStream pos, long nowElapsed);
+
+    /**
+     * A functional interface used to update the alarm. Used to describe the update in
+     * {@link #recalculateAlarmDeliveries(AlarmDeliveryCalculator)}
+     */
+    @FunctionalInterface
+    interface AlarmDeliveryCalculator {
+        /**
+         * Updates the given alarm's delivery time.
+         *
+         * @param a the alarm to update.
+         * @return {@code true} if any change was made, {@code false} otherwise.
+         */
+        boolean updateAlarmDelivery(Alarm a);
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
new file mode 100644
index 0000000..a08c222
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2020 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.alarm;
+
+import static com.android.server.alarm.AlarmManagerService.DEBUG_BATCH;
+import static com.android.server.alarm.AlarmManagerService.TAG;
+import static com.android.server.alarm.AlarmManagerService.clampPositive;
+import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
+import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
+
+import android.app.AlarmManager;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.StatLogger;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.function.Predicate;
+
+/**
+ * Batching implementation of an Alarm Store.
+ * This keeps the alarms in batches, which are sorted on the start time of their delivery window.
+ */
+public class BatchingAlarmStore implements AlarmStore {
+
+    private ArrayList<Batch> mAlarmBatches = new ArrayList<>();
+    private int mSize;
+    private AlarmClockRemovalListener mAlarmClockRemovalListener;
+
+    interface Stats {
+        int REBATCH_ALL_ALARMS = 1;
+    }
+
+    final StatLogger mStatLogger = new StatLogger("Alarm store stats", new String[]{
+            "REBATCH_ALL_ALARMS",
+    });
+
+    private static final Comparator<Batch> sBatchOrder = (b1, b2) -> {
+        long when1 = b1.mStart;
+        long when2 = b2.mStart;
+        if (when1 > when2) {
+            return 1;
+        }
+        if (when1 < when2) {
+            return -1;
+        }
+        return 0;
+    };
+
+    private static final Comparator<Alarm> sIncreasingTimeOrder = (a1, a2) -> {
+        long when1 = a1.whenElapsed;
+        long when2 = a2.whenElapsed;
+        if (when1 > when2) {
+            return 1;
+        }
+        if (when1 < when2) {
+            return -1;
+        }
+        return 0;
+    };
+
+    BatchingAlarmStore(AlarmClockRemovalListener listener) {
+        mAlarmClockRemovalListener = listener;
+    }
+
+    @Override
+    public void add(Alarm a) {
+        insertAndBatchAlarm(a);
+        mSize++;
+    }
+
+    @Override
+    public ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms) {
+        final ArrayList<Alarm> removed = new ArrayList<>();
+        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+            final Batch b = mAlarmBatches.get(i);
+            removed.addAll(b.remove(whichAlarms));
+            if (b.size() == 0) {
+                mAlarmBatches.remove(i);
+            }
+        }
+        if (!removed.isEmpty()) {
+            mSize -= removed.size();
+            rebatchAllAlarms();
+        }
+        return removed;
+    }
+
+    private void rebatchAllAlarms() {
+        final long start = mStatLogger.getTime();
+        final ArrayList<Batch> oldBatches = (ArrayList<Batch>) mAlarmBatches.clone();
+        mAlarmBatches.clear();
+        for (final Batch batch : oldBatches) {
+            for (int i = 0; i < batch.size(); i++) {
+                insertAndBatchAlarm(batch.get(i));
+            }
+        }
+        mStatLogger.logDurationStat(Stats.REBATCH_ALL_ALARMS, start);
+    }
+
+    @Override
+    public int size() {
+        return mSize;
+    }
+
+    @Override
+    public long getNextWakeupDeliveryTime() {
+        for (Batch b : mAlarmBatches) {
+            if (b.hasWakeups()) {
+                return b.mStart;
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public long getNextDeliveryTime() {
+        if (mAlarmBatches.size() > 0) {
+            return mAlarmBatches.get(0).mStart;
+        }
+        return 0;
+    }
+
+    @Override
+    public ArrayList<Alarm> removePendingAlarms(long nowElapsed) {
+        final ArrayList<Alarm> removedAlarms = new ArrayList<>();
+        while (mAlarmBatches.size() > 0) {
+            final Batch batch = mAlarmBatches.get(0);
+            if (batch.mStart > nowElapsed) {
+                break;
+            }
+            mAlarmBatches.remove(0);
+            for (int i = 0; i < batch.size(); i++) {
+                removedAlarms.add(batch.get(i));
+            }
+        }
+        mSize -= removedAlarms.size();
+        return removedAlarms;
+    }
+
+    @Override
+    public boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
+        boolean changed = false;
+        for (final Batch b : mAlarmBatches) {
+            for (int i = 0; i < b.size(); i++) {
+                changed |= deliveryCalculator.updateAlarmDelivery(b.get(i));
+            }
+        }
+        if (changed) {
+            rebatchAllAlarms();
+        }
+        return changed;
+    }
+
+    @Override
+    public ArrayList<Alarm> asList() {
+        final ArrayList<Alarm> allAlarms = new ArrayList<>();
+        for (final Batch batch : mAlarmBatches) {
+            for (int i = 0; i < batch.size(); i++) {
+                allAlarms.add(batch.get(i));
+            }
+        }
+        return allAlarms;
+    }
+
+    @Override
+    public void dump(IndentingPrintWriter ipw, long nowElapsed, SimpleDateFormat sdf) {
+        ipw.print("Pending alarm batches: ");
+        ipw.println(mAlarmBatches.size());
+        for (Batch b : mAlarmBatches) {
+            ipw.print(b);
+            ipw.println(':');
+            ipw.increaseIndent();
+            dumpAlarmList(ipw, b.mAlarms, nowElapsed, sdf);
+            ipw.decreaseIndent();
+        }
+        mStatLogger.dump(ipw);
+    }
+
+    @Override
+    public void dumpProto(ProtoOutputStream pos, long nowElapsed) {
+        for (Batch b : mAlarmBatches) {
+            b.dumpDebug(pos, AlarmManagerServiceDumpProto.PENDING_ALARM_BATCHES, nowElapsed);
+        }
+    }
+
+    private void insertAndBatchAlarm(Alarm alarm) {
+        final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
+                : attemptCoalesce(alarm.whenElapsed, alarm.maxWhenElapsed);
+
+        if (whichBatch < 0) {
+            addBatch(mAlarmBatches, new Batch(alarm));
+        } else {
+            final Batch batch = mAlarmBatches.get(whichBatch);
+            if (batch.add(alarm)) {
+                // The start time of this batch advanced, so batch ordering may
+                // have just been broken.  Move it to where it now belongs.
+                mAlarmBatches.remove(whichBatch);
+                addBatch(mAlarmBatches, batch);
+            }
+        }
+    }
+
+    static void addBatch(ArrayList<Batch> list, Batch newBatch) {
+        int index = Collections.binarySearch(list, newBatch, sBatchOrder);
+        if (index < 0) {
+            index = 0 - index - 1;
+        }
+        list.add(index, newBatch);
+    }
+
+    // Return the index of the matching batch, or -1 if none found.
+    private int attemptCoalesce(long whenElapsed, long maxWhen) {
+        final int n = mAlarmBatches.size();
+        for (int i = 0; i < n; i++) {
+            Batch b = mAlarmBatches.get(i);
+            if ((b.mFlags & AlarmManager.FLAG_STANDALONE) == 0 && b.canHold(whenElapsed, maxWhen)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    final class Batch {
+        long mStart;     // These endpoints are always in ELAPSED
+        long mEnd;
+        int mFlags;      // Flags for alarms, such as FLAG_STANDALONE.
+
+        final ArrayList<Alarm> mAlarms = new ArrayList<>();
+
+        Batch(Alarm seed) {
+            mStart = seed.whenElapsed;
+            mEnd = clampPositive(seed.maxWhenElapsed);
+            mFlags = seed.flags;
+            mAlarms.add(seed);
+        }
+
+        int size() {
+            return mAlarms.size();
+        }
+
+        Alarm get(int index) {
+            return mAlarms.get(index);
+        }
+
+        boolean canHold(long whenElapsed, long maxWhen) {
+            return (mEnd >= whenElapsed) && (mStart <= maxWhen);
+        }
+
+        boolean add(Alarm alarm) {
+            boolean newStart = false;
+            // narrows the batch if necessary; presumes that canHold(alarm) is true
+            int index = Collections.binarySearch(mAlarms, alarm, sIncreasingTimeOrder);
+            if (index < 0) {
+                index = 0 - index - 1;
+            }
+            mAlarms.add(index, alarm);
+            if (DEBUG_BATCH) {
+                Slog.v(TAG, "Adding " + alarm + " to " + this);
+            }
+            if (alarm.whenElapsed > mStart) {
+                mStart = alarm.whenElapsed;
+                newStart = true;
+            }
+            if (alarm.maxWhenElapsed < mEnd) {
+                mEnd = alarm.maxWhenElapsed;
+            }
+            mFlags |= alarm.flags;
+
+            if (DEBUG_BATCH) {
+                Slog.v(TAG, "    => now " + this);
+            }
+            return newStart;
+        }
+
+        ArrayList<Alarm> remove(Predicate<Alarm> predicate) {
+            final ArrayList<Alarm> removed = new ArrayList<>();
+            long newStart = 0;  // recalculate endpoints as we go
+            long newEnd = Long.MAX_VALUE;
+            int newFlags = 0;
+            for (int i = 0; i < mAlarms.size(); ) {
+                Alarm alarm = mAlarms.get(i);
+                if (predicate.test(alarm)) {
+                    removed.add(mAlarms.remove(i));
+                    if (alarm.alarmClock != null && mAlarmClockRemovalListener != null) {
+                        mAlarmClockRemovalListener.onRemoved();
+                    }
+                    if (isTimeTickAlarm(alarm)) {
+                        // This code path is not invoked when delivering alarms, only when removing
+                        // alarms due to the caller cancelling it or getting uninstalled, etc.
+                        Slog.wtf(TAG, "Removed TIME_TICK alarm");
+                    }
+                } else {
+                    if (alarm.whenElapsed > newStart) {
+                        newStart = alarm.whenElapsed;
+                    }
+                    if (alarm.maxWhenElapsed < newEnd) {
+                        newEnd = alarm.maxWhenElapsed;
+                    }
+                    newFlags |= alarm.flags;
+                    i++;
+                }
+            }
+            if (!removed.isEmpty()) {
+                // commit the new batch bounds
+                mStart = newStart;
+                mEnd = newEnd;
+                mFlags = newFlags;
+            }
+            return removed;
+        }
+
+        boolean hasWakeups() {
+            final int n = mAlarms.size();
+            for (int i = 0; i < n; i++) {
+                Alarm a = mAlarms.get(i);
+                if (a.wakeup) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder b = new StringBuilder(40);
+            b.append("Batch{");
+            b.append(Integer.toHexString(this.hashCode()));
+            b.append(" num=");
+            b.append(size());
+            b.append(" start=");
+            b.append(mStart);
+            b.append(" end=");
+            b.append(mEnd);
+            if (mFlags != 0) {
+                b.append(" flgs=0x");
+                b.append(Integer.toHexString(mFlags));
+            }
+            b.append('}');
+            return b.toString();
+        }
+
+        public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) {
+            final long token = proto.start(fieldId);
+
+            proto.write(BatchProto.START_REALTIME, mStart);
+            proto.write(BatchProto.END_REALTIME, mEnd);
+            proto.write(BatchProto.FLAGS, mFlags);
+            for (Alarm a : mAlarms) {
+                a.dumpDebug(proto, BatchProto.ALARMS, nowElapsed);
+            }
+
+            proto.end(token);
+        }
+    }
+
+    @FunctionalInterface
+    interface AlarmClockRemovalListener {
+        void onRemoved();
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index f1c624d..67997cf 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -22,6 +22,7 @@
 
 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 
+import android.annotation.Nullable;
 import android.app.job.JobInfo;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -86,9 +87,12 @@
     @GuardedBy("mLock")
     private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>();
 
-    /** List of currently available networks. */
+    /**
+     * Set of currently available networks mapped to their latest network capabilities. Cache the
+     * latest capabilities to avoid unnecessary calls into ConnectivityManager.
+     */
     @GuardedBy("mLock")
-    private final ArraySet<Network> mAvailableNetworks = new ArraySet<>();
+    private final ArrayMap<Network, NetworkCapabilities> mAvailableNetworks = new ArrayMap<>();
 
     private static final int MSG_DATA_SAVER_TOGGLED = 0;
     private static final int MSG_UID_RULES_CHANGES = 1;
@@ -165,9 +169,8 @@
     public boolean isNetworkAvailable(JobStatus job) {
         synchronized (mLock) {
             for (int i = 0; i < mAvailableNetworks.size(); ++i) {
-                final Network network = mAvailableNetworks.valueAt(i);
-                final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(
-                        network);
+                final Network network = mAvailableNetworks.keyAt(i);
+                final NetworkCapabilities capabilities = mAvailableNetworks.valueAt(i);
                 final boolean satisfied = isSatisfied(job, network, capabilities, mConstants);
                 if (DEBUG) {
                     Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network
@@ -427,9 +430,33 @@
         return false;
     }
 
+    @Nullable
+    private NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
+        if (network == null) {
+            return null;
+        }
+        synchronized (mLock) {
+            // There is technically a race here if the Network object is reused. This can happen
+            // only if that Network disconnects and the auto-incrementing network ID in
+            // ConnectivityService wraps. This should no longer be a concern if/when we only make
+            // use of asynchronous calls.
+            if (mAvailableNetworks.get(network) != null) {
+                return mAvailableNetworks.get(network);
+            }
+
+            // This should almost never happen because any time a new network connects, the
+            // NetworkCallback would populate mAvailableNetworks. However, it's currently necessary
+            // because we also call synchronous methods such as getActiveNetworkForUid.
+            // TODO(134978280): remove after switching to callback-based APIs
+            final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
+            mAvailableNetworks.put(network, capabilities);
+            return capabilities;
+        }
+    }
+
     private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
         final Network network = mConnManager.getActiveNetworkForUid(jobStatus.getSourceUid());
-        final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
+        final NetworkCapabilities capabilities = getNetworkCapabilities(network);
         return updateConstraintsSatisfied(jobStatus, network, capabilities);
     }
 
@@ -470,19 +497,13 @@
      */
     private void updateTrackedJobs(int filterUid, Network filterNetwork) {
         synchronized (mLock) {
-            // Since this is a really hot codepath, temporarily cache any
-            // answers that we get from ConnectivityManager.
-            final ArrayMap<Network, NetworkCapabilities> networkToCapabilities = new ArrayMap<>();
-
             boolean changed = false;
             if (filterUid == -1) {
                 for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
-                    changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i),
-                            filterNetwork, networkToCapabilities);
+                    changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork);
                 }
             } else {
-                changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid),
-                        filterNetwork, networkToCapabilities);
+                changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork);
             }
             if (changed) {
                 mStateChangedListener.onControllerStateChanged();
@@ -490,18 +511,13 @@
         }
     }
 
-    private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork,
-            ArrayMap<Network, NetworkCapabilities> networkToCapabilities) {
+    private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) {
         if (jobs == null || jobs.size() == 0) {
             return false;
         }
 
         final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid());
-        NetworkCapabilities capabilities = networkToCapabilities.get(network);
-        if (capabilities == null) {
-            capabilities = mConnManager.getNetworkCapabilities(network);
-            networkToCapabilities.put(network, capabilities);
-        }
+        final NetworkCapabilities capabilities = getNetworkCapabilities(network);
         final boolean networkMatch = (filterNetwork == null
                 || Objects.equals(filterNetwork, network));
 
@@ -544,9 +560,9 @@
         @Override
         public void onAvailable(Network network) {
             if (DEBUG) Slog.v(TAG, "onAvailable: " + network);
-            synchronized (mLock) {
-                mAvailableNetworks.add(network);
-            }
+            // Documentation says not to call getNetworkCapabilities here but wait for
+            // onCapabilitiesChanged instead.  onCapabilitiesChanged should be called immediately
+            // after this, so no need to update mAvailableNetworks here.
         }
 
         @Override
@@ -554,6 +570,9 @@
             if (DEBUG) {
                 Slog.v(TAG, "onCapabilitiesChanged: " + network);
             }
+            synchronized (mLock) {
+                mAvailableNetworks.put(network, capabilities);
+            }
             updateTrackedJobs(-1, network);
         }
 
@@ -630,6 +649,8 @@
             pw.println("Available networks:");
             pw.increaseIndent();
             for (int i = 0; i < mAvailableNetworks.size(); i++) {
+                pw.print(mAvailableNetworks.keyAt(i));
+                pw.print(": ");
                 pw.println(mAvailableNetworks.valueAt(i));
             }
             pw.decreaseIndent();
@@ -667,7 +688,7 @@
                     mRequestedWhitelistJobs.keyAt(i));
         }
         for (int i = 0; i < mAvailableNetworks.size(); i++) {
-            Network network = mAvailableNetworks.valueAt(i);
+            Network network = mAvailableNetworks.keyAt(i);
             if (network != null) {
                 network.dumpDebug(proto,
                         StateControllerProto.ConnectivityController.AVAILABLE_NETWORKS);
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 4417b68..ce4b030 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -44,7 +44,6 @@
     plugins: ["java_api_finder"],
 
     hostdex: true, // for hiddenapi check
-    visibility: ["//frameworks/av/apex:__subpackages__"],
     apex_available: [
         "com.android.media",
         "test_com.android.media",
@@ -83,7 +82,7 @@
         "java/android/media/MediaParser.java"
     ],
     path: "java",
-}	
+}
 
 java_sdk_library {
     name: "framework-media",
@@ -99,15 +98,7 @@
     libs: [
         "framework_media_annotation",
     ],
-
-    // Allow access to the stubs from anywhere.
-    visibility: ["//visibility:public"],
-
-    // Restrict access to implementation library.
-    impl_library_visibility: [
-       "//visibility:override", // Ignore the visibility property.
-       "//frameworks/av/apex:__subpackages__",
-    ],
+    impl_library_visibility: ["//frameworks/av/apex:__subpackages__"],
 }
 
 
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index be553fe..c0560f6 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -25,14 +25,8 @@
     name: "framework-permission",
     defaults: ["framework-module-defaults"],
 
-    // Allow access to the stubs from anywhere.
-    visibility: ["//visibility:public"],
-
     // Restrict access to implementation library.
-    impl_library_visibility: [
-        "//visibility:override", // Ignore the visibility property.
-        "//frameworks/base/apex/permission:__subpackages__",
-    ],
+    impl_library_visibility: ["//frameworks/base/apex/permission:__subpackages__"],
 
     srcs: [
         ":framework-permission-sources",
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 7f31879..b7d8433 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -23,15 +23,7 @@
 java_sdk_library {
     name: "service-permission",
     defaults: ["framework-system-server-module-defaults"],
-    visibility: [
-        "//frameworks/base/services/core",
-        "//frameworks/base/apex/permission",
-        "//frameworks/base/apex/permission/testing",
-        "//frameworks/base/apex/permission/tests",
-        "//frameworks/base/services/tests/mockingservicestests",
-    ],
     impl_library_visibility: [
-        "//visibility:override",
         "//frameworks/base/apex/permission/tests",
         "//frameworks/base/services/tests/mockingservicestests",
         "//frameworks/base/services/tests/servicestests",
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index e75fa88..ede8852 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -19,8 +19,10 @@
 }
 
 apex_defaults {
-    native_shared_libs: [
+    jni_libs: [
         "libstats_jni",
+    ],
+    native_shared_libs: [
         "libstatspull",
         "libstatssocket",
     ],
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index b06f401..bf4323d 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -72,19 +72,7 @@
 
     hostdex: true, // for hiddenapi check
 
-    visibility: [
-        "//frameworks/base", // Framework
-        "//frameworks/base/apex/statsd:__subpackages__", // statsd apex
-        "//frameworks/base/packages/Tethering", // Tethering
-        "//frameworks/opt/net/wifi/service", // wifi service
-        "//packages/providers/MediaProvider", // MediaProvider apk
-    ],
-
-    // Restrict access to implementation library.
-    impl_library_visibility: [
-        "//visibility:override", // Ignore the visibility property.
-        "//frameworks/base/apex/statsd:__subpackages__", // statsd apex
-    ],
+    impl_library_visibility: ["//frameworks/base/apex/statsd/framework/test:__subpackages__"],
 
     apex_available: [
         "com.android.os.statsd",
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index 97846f2..1e3846b 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -27,6 +27,7 @@
 import android.os.IPullAtomCallback;
 import android.os.IStatsManagerService;
 import android.os.IStatsd;
+import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
@@ -412,8 +413,13 @@
     @Override
     public byte[] getData(long key, String packageName) throws IllegalStateException {
         enforceDumpAndUsageStatsPermission(packageName);
+        PowerManager powerManager = (PowerManager)
+                mContext.getSystemService(Context.POWER_SERVICE);
+        PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                /*tag=*/ StatsManagerService.class.getCanonicalName());
         int callingUid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
+        wl.acquire();
         try {
             IStatsd statsd = waitForStatsd();
             if (statsd != null) {
@@ -423,6 +429,7 @@
             Log.e(TAG, "Failed to getData with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
+            wl.release();
             Binder.restoreCallingIdentity(token);
         }
         throw new IllegalStateException("Failed to connect to statsd to getData");
diff --git a/api/current.txt b/api/current.txt
index c6af6be..e678d16 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1005,7 +1005,7 @@
     field public static final int numericModifiers = 16844111; // 0x101054f
     field public static final int numericShortcut = 16843236; // 0x10101e4
     field public static final int offset = 16844052; // 0x1010514
-    field public static final int onClick = 16843375; // 0x101026f
+    field @Deprecated public static final int onClick = 16843375; // 0x101026f
     field public static final int oneshot = 16843159; // 0x1010197
     field public static final int opacity = 16843550; // 0x101031e
     field public static final int opticalInsetBottom = 16844171; // 0x101058b
@@ -6188,6 +6188,7 @@
     method public android.app.PictureInPictureParams build();
     method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
     method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
+    method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterAllowed(boolean);
     method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
   }
 
@@ -11617,6 +11618,16 @@
     field public int version;
   }
 
+  public final class FileChecksum implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getKind();
+    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getSplitName();
+    method @NonNull public byte[] getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
+  }
+
   public final class InstallSourceInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getInitiatingPackageName();
@@ -11992,6 +12003,7 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
+    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12082,6 +12094,7 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
+    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12236,6 +12249,8 @@
     field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
     field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12245,9 +12260,16 @@
     field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int SYNCHRONOUS = 2; // 0x2
+    field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
+    field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
+    field public static final int WHOLE_MD5 = 2; // 0x2
+    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field public static final int WHOLE_SHA1 = 4; // 0x4
+    field public static final int WHOLE_SHA256 = 8; // 0x8
+    field public static final int WHOLE_SHA512 = 16; // 0x10
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -31644,6 +31666,7 @@
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setBssid(@NonNull android.net.MacAddress);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setCredentialSharedWithUser(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean);
+    method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedMacRandomizationEnabled(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsInitialAutojoinEnabled(boolean);
@@ -31715,6 +31738,7 @@
     method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
     method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
     method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
+    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
     method public void onSessionConfigFailed();
     method public void onSessionConfigUpdated();
     method public void onSessionTerminated();
@@ -31790,6 +31814,7 @@
     method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @NonNull android.net.wifi.aware.IdentityChangedListener, @Nullable android.os.Handler);
     method public android.net.wifi.aware.Characteristics getCharacteristics();
     method public boolean isAvailable();
+    method public boolean isDeviceAttached();
     field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
@@ -48377,7 +48402,8 @@
     method public boolean isVoiceCapable();
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
-    method public void listen(android.telephony.PhoneStateListener, int);
+    method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
+    method public void listen(long, @NonNull android.telephony.PhoneStateListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 17545a4..097609e 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -35,13 +35,24 @@
 package android.media {
 
   public class AudioManager {
+    method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+    method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+    method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public static final class MediaMetadata.Builder {
+    ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+  }
+
 }
 
 package android.media.session {
 
+  public static final class MediaController.PlaybackInfo implements android.os.Parcelable {
+    ctor public MediaController.PlaybackInfo(int, int, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.media.AudioAttributes, @Nullable String);
+  }
+
   public final class MediaSession {
     field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000
   }
@@ -129,6 +140,14 @@
 
 }
 
+package android.provider {
+
+  public final class DeviceConfig {
+    field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
+  }
+
+}
+
 package android.util {
 
   public class AtomicFile {
diff --git a/api/system-current.txt b/api/system-current.txt
index 41e8593..3ec3467 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -219,6 +219,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
@@ -1740,6 +1741,7 @@
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
+    field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
     field public static final String NETD_SERVICE = "netd";
     field public static final String NETWORK_SCORE_SERVICE = "network_score";
     field public static final String OEM_LOCK_SERVICE = "oem_lock";
@@ -4197,8 +4199,10 @@
   public class AudioManager {
     method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
     method public void clearAudioServerStateCallback();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
@@ -4209,6 +4213,7 @@
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -4217,6 +4222,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
@@ -4226,6 +4232,7 @@
     method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
@@ -4255,6 +4262,10 @@
     method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
   }
 
+  public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener {
+    method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
+  }
+
   public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
     method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
   }
@@ -4332,6 +4343,65 @@
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
   }
 
+  public final class MediaTranscodeManager {
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+    field public static final int PRIORITY_REALTIME = 1; // 0x1
+    field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
+  }
+
+  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
+    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
+  }
+
+  public static final class MediaTranscodeManager.TranscodingJob {
+    method public void cancel();
+    method public int getJobId();
+    method @IntRange(from=0, to=100) public int getProgress();
+    method public int getResult();
+    method public int getStatus();
+    method public boolean retry();
+    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+    method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+    field public static final int RESULT_CANCELED = 4; // 0x4
+    field public static final int RESULT_ERROR = 3; // 0x3
+    field public static final int RESULT_NONE = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 2; // 0x2
+    field public static final int STATUS_FINISHED = 3; // 0x3
+    field public static final int STATUS_PAUSED = 4; // 0x4
+    field public static final int STATUS_PENDING = 1; // 0x1
+    field public static final int STATUS_RUNNING = 2; // 0x2
+  }
+
+  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
+    method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingJob, @IntRange(from=0, to=100) int);
+  }
+
+  public static final class MediaTranscodeManager.TranscodingRequest {
+    method @NonNull public android.net.Uri getDestinationUri();
+    method public int getPriority();
+    method @NonNull public android.net.Uri getSourceUri();
+    method public int getType();
+    method @Nullable public android.media.MediaFormat getVideoTrackFormat();
+  }
+
+  public static final class MediaTranscodeManager.TranscodingRequest.Builder {
+    ctor public MediaTranscodeManager.TranscodingRequest.Builder();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
+  }
+
+  public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
+    ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver();
+    method @Nullable public android.media.MediaFormat resolveVideoFormat();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat);
+    method public boolean shouldTranscode();
+    field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+  }
+
   public class PlayerProxy {
     method public void pause();
     method public void setPan(float);
@@ -7820,7 +7890,7 @@
     method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
     method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
-    method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
+    method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
     method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SendMgmtFrameCallback);
     method public void setOnServiceDeadCallback(@NonNull Runnable);
     method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
@@ -7871,10 +7941,10 @@
     field public final int txBitrateMbps;
   }
 
-  public static interface WifiNl80211Manager.SoftApCallback {
-    method public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
-    method public void onFailure();
-    method public void onSoftApChannelSwitched(int, int);
+  @Deprecated public static interface WifiNl80211Manager.SoftApCallback {
+    method @Deprecated public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
+    method @Deprecated public void onFailure();
+    method @Deprecated public void onSoftApChannelSwitched(int, int);
   }
 
   public static class WifiNl80211Manager.TxPacketCounters {
@@ -10820,6 +10890,7 @@
     method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
     method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+    method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
@@ -10827,12 +10898,29 @@
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 4294967296L; // 0x100000000L
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
+  public final class PhysicalChannelConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCellBandwidthDownlink();
+    method public int getChannelNumber();
+    method public int getConnectionStatus();
+    method public int getNetworkType();
+    method @IntRange(from=0, to=1007) public int getPhysicalCellId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
+    field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+    field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+    field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
+    field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
+  }
+
   public final class PreciseCallState implements android.os.Parcelable {
     ctor public PreciseCallState(int, int, int, int, int);
     method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index 529dcf7..3de1d93 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1819,6 +1819,65 @@
     method @NonNull public String getOriginalId();
   }
 
+  public final class MediaTranscodeManager {
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+    field public static final int PRIORITY_REALTIME = 1; // 0x1
+    field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
+  }
+
+  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
+    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
+  }
+
+  public static final class MediaTranscodeManager.TranscodingJob {
+    method public void cancel();
+    method public int getJobId();
+    method @IntRange(from=0, to=100) public int getProgress();
+    method public int getResult();
+    method public int getStatus();
+    method public boolean retry();
+    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+    method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+    field public static final int RESULT_CANCELED = 4; // 0x4
+    field public static final int RESULT_ERROR = 3; // 0x3
+    field public static final int RESULT_NONE = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 2; // 0x2
+    field public static final int STATUS_FINISHED = 3; // 0x3
+    field public static final int STATUS_PAUSED = 4; // 0x4
+    field public static final int STATUS_PENDING = 1; // 0x1
+    field public static final int STATUS_RUNNING = 2; // 0x2
+  }
+
+  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
+    method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingJob, @IntRange(from=0, to=100) int);
+  }
+
+  public static final class MediaTranscodeManager.TranscodingRequest {
+    method @NonNull public android.net.Uri getDestinationUri();
+    method public int getPriority();
+    method @NonNull public android.net.Uri getSourceUri();
+    method public int getType();
+    method @Nullable public android.media.MediaFormat getVideoTrackFormat();
+  }
+
+  public static final class MediaTranscodeManager.TranscodingRequest.Builder {
+    ctor public MediaTranscodeManager.TranscodingRequest.Builder();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
+  }
+
+  public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
+    ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver();
+    method @Nullable public android.media.MediaFormat resolveVideoFormat();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat);
+    method public boolean shouldTranscode();
+    field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+  }
+
   public final class PlaybackParams implements android.os.Parcelable {
     method public int getAudioStretchMode();
     method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -3240,6 +3299,7 @@
     field public static final String NAMESPACE_AUTOFILL = "autofill";
     field public static final String NAMESPACE_BIOMETRICS = "biometrics";
     field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+    field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
     field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
     field public static final String NAMESPACE_PERMISSIONS = "permissions";
     field public static final String NAMESPACE_PRIVACY = "privacy";
@@ -5569,18 +5629,18 @@
 
   public class TaskOrganizer extends android.window.WindowOrganizer {
     ctor public TaskOrganizer();
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static android.app.ActivityManager.RunningTaskInfo createRootTask(int, int);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static boolean deleteRootTask(@NonNull android.window.WindowContainerToken);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static android.window.WindowContainerToken getImeTarget(int);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.app.ActivityManager.RunningTaskInfo createRootTask(int, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.WindowContainerToken getImeTarget(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]);
     method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
     method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer();
   }
 
@@ -5617,7 +5677,7 @@
   public class WindowOrganizer {
     ctor public WindowOrganizer();
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static void applyTransaction(@NonNull android.window.WindowContainerTransaction);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
   }
 
 }
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 1e200c5..437a87e 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -27,9 +27,9 @@
 #include <cstring>
 #include <memory>
 
-#include <android/log.h>
 #include <android/looper.h>
 #include <jni.h>
+#include <log/log.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
@@ -37,10 +37,8 @@
 
 #include <android-base/stringprintf.h>
 
-#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
-#define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
-#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
-#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+// Log debug messages about the output.
+static constexpr bool DEBUG_OUTPUT = false;
 
 namespace android {
 namespace uhid {
@@ -61,7 +59,7 @@
 
 static void checkAndClearException(JNIEnv* env, const char* methodName) {
     if (env->ExceptionCheck()) {
-        LOGE("An exception was thrown by callback '%s'.", methodName);
+        ALOGE("An exception was thrown by callback '%s'.", methodName);
         env->ExceptionClear();
     }
 }
@@ -115,9 +113,9 @@
     checkAndClearException(env, "onDeviceGetReport");
 }
 
-void DeviceCallback::onDeviceOutput(const std::vector<uint8_t>& data) {
+void DeviceCallback::onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data) {
     JNIEnv* env = getJNIEnv();
-    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput,
+    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType,
                         toJbyteArray(env, data).get());
     checkAndClearException(env, "onDeviceOutput");
 }
@@ -133,13 +131,13 @@
                                      std::unique_ptr<DeviceCallback> callback) {
     size_t size = descriptor.size();
     if (size > HID_MAX_DESCRIPTOR_SIZE) {
-        LOGE("Received invalid hid report with descriptor size %zu, skipping", size);
+        ALOGE("Received invalid hid report with descriptor size %zu, skipping", size);
         return nullptr;
     }
 
     android::base::unique_fd fd(::open(UHID_PATH, O_RDWR | O_CLOEXEC));
     if (!fd.ok()) {
-        LOGE("Failed to open uhid: %s", strerror(errno));
+        ALOGE("Failed to open uhid: %s", strerror(errno));
         return nullptr;
     }
 
@@ -159,14 +157,14 @@
     errno = 0;
     ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
     if (ret < 0 || ret != sizeof(ev)) {
-        LOGE("Failed to create uhid node: %s", strerror(errno));
+        ALOGE("Failed to create uhid node: %s", strerror(errno));
         return nullptr;
     }
 
     // Wait for the device to actually be created.
     ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev)));
     if (ret < 0 || ev.type != UHID_START) {
-        LOGE("uhid node failed to start: %s", strerror(errno));
+        ALOGE("uhid node failed to start: %s", strerror(errno));
         return nullptr;
     }
     // using 'new' to access non-public constructor
@@ -177,7 +175,7 @@
       : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
     ALooper* aLooper = ALooper_forThread();
     if (aLooper == NULL) {
-        LOGE("Could not get ALooper, ALooper_forThread returned NULL");
+        ALOGE("Could not get ALooper, ALooper_forThread returned NULL");
         aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
     }
     ALooper_addFd(aLooper, mFd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents,
@@ -189,7 +187,7 @@
     if (looper != NULL) {
         ALooper_removeFd(looper, mFd);
     } else {
-        LOGE("Could not remove fd, ALooper_forThread() returned NULL!");
+        ALOGE("Could not remove fd, ALooper_forThread() returned NULL!");
     }
     struct uhid_event ev = {};
     ev.type = UHID_DESTROY;
@@ -200,13 +198,13 @@
 static void writeEvent(int fd, struct uhid_event& ev, const char* messageType) {
     ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
     if (ret < 0 || ret != sizeof(ev)) {
-        LOGE("Failed to send uhid_event %s: %s", messageType, strerror(errno));
+        ALOGE("Failed to send uhid_event %s: %s", messageType, strerror(errno));
     }
 }
 
 void Device::sendReport(const std::vector<uint8_t>& report) const {
     if (report.size() > UHID_DATA_MAX) {
-        LOGE("Received invalid report of size %zu, skipping", report.size());
+        ALOGE("Received invalid report of size %zu, skipping", report.size());
         return;
     }
 
@@ -230,14 +228,14 @@
 
 int Device::handleEvents(int events) {
     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
-        LOGE("uhid node was closed or an error occurred. events=0x%x", events);
+        ALOGE("uhid node was closed or an error occurred. events=0x%x", events);
         mDeviceCallback->onDeviceError();
         return 0;
     }
     struct uhid_event ev;
     ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev)));
     if (ret < 0) {
-        LOGE("Failed to read from uhid node: %s", strerror(errno));
+        ALOGE("Failed to read from uhid node: %s", strerror(errno));
         mDeviceCallback->onDeviceError();
         return 0;
     }
@@ -254,23 +252,28 @@
         case UHID_SET_REPORT: {
             const struct uhid_set_report_req& set_report = ev.u.set_report;
             if (set_report.size > UHID_DATA_MAX) {
-                LOGE("SET_REPORT contains too much data: size = %" PRIu16, set_report.size);
+                ALOGE("SET_REPORT contains too much data: size = %" PRIu16, set_report.size);
                 return 0;
             }
 
             std::vector<uint8_t> data(set_report.data, set_report.data + set_report.size);
-            LOGI("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
-                 set_report.rnum, toString(data).c_str());
+            if (DEBUG_OUTPUT) {
+                ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
+                      set_report.rnum, toString(data).c_str());
+            }
             break;
         }
         case UHID_OUTPUT: {
             struct uhid_output_req& output = ev.u.output;
             std::vector<uint8_t> data(output.data, output.data + output.size);
-            mDeviceCallback->onDeviceOutput(data);
+            if (DEBUG_OUTPUT) {
+                ALOGD("UHID_OUTPUT rtype=%" PRIu8 " data=%s", output.rtype, toString(data).c_str());
+            }
+            mDeviceCallback->onDeviceOutput(output.rtype, data);
             break;
         }
         default: {
-            LOGI("Unhandled event type: %" PRIu32, ev.type);
+            ALOGI("Unhandled event type: %" PRIu32, ev.type);
             break;
         }
     }
@@ -318,7 +321,7 @@
     if (d) {
         d->sendReport(report);
     } else {
-        LOGE("Could not send report, Device* is null!");
+        ALOGE("Could not send report, Device* is null!");
     }
 }
 
@@ -329,7 +332,7 @@
         std::vector<uint8_t> report = getData(env, rawReport);
         d->sendGetFeatureReportReply(id, report);
     } else {
-        LOGE("Could not send get feature report reply, Device* is null!");
+        ALOGE("Could not send get feature report reply, Device* is null!");
     }
 }
 
@@ -354,7 +357,7 @@
 int register_com_android_commands_hid_Device(JNIEnv* env) {
     jclass clazz = env->FindClass("com/android/commands/hid/Device$DeviceCallback");
     if (clazz == NULL) {
-        LOGE("Unable to find class 'DeviceCallback'");
+        ALOGE("Unable to find class 'DeviceCallback'");
         return JNI_ERR;
     }
     uhid::gDeviceCallbackClassInfo.onDeviceOpen =
@@ -362,12 +365,12 @@
     uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
             env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
     uhid::gDeviceCallbackClassInfo.onDeviceOutput =
-            env->GetMethodID(clazz, "onDeviceOutput", "([B)V");
+            env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V");
     uhid::gDeviceCallbackClassInfo.onDeviceError =
             env->GetMethodID(clazz, "onDeviceError", "()V");
     if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
             uhid::gDeviceCallbackClassInfo.onDeviceError == NULL) {
-        LOGE("Unable to obtain onDeviceOpen or onDeviceError methods");
+        ALOGE("Unable to obtain onDeviceOpen or onDeviceError methods");
         return JNI_ERR;
     }
 
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 7202b45..5483b40 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -31,7 +31,7 @@
 
     void onDeviceOpen();
     void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
-    void onDeviceOutput(const std::vector<uint8_t>& data);
+    void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data);
     void onDeviceError();
 
 private:
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index dade415..20b4bd8 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -26,6 +26,12 @@
 
 import com.android.internal.os.SomeArgs;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Map;
@@ -38,12 +44,16 @@
     private static final int MSG_SEND_GET_FEATURE_REPORT_REPLY = 3;
     private static final int MSG_CLOSE_DEVICE = 4;
 
+    // Sync with linux uhid_event_type::UHID_OUTPUT
+    private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6;
+
     private final int mId;
     private final HandlerThread mThread;
     private final DeviceHandler mHandler;
     // mFeatureReports is limited to 256 entries, because the report number is 8-bit
     private final SparseArray<byte[]> mFeatureReports;
     private final Map<ByteBuffer, byte[]> mOutputs;
+    private final OutputStream mOutputStream;
     private long mTimeToSend;
 
     private final Object mCond = new Object();
@@ -66,6 +76,7 @@
         mHandler = new DeviceHandler(mThread.getLooper());
         mFeatureReports = featureReports;
         mOutputs = outputs;
+        mOutputStream = System.out;
         SomeArgs args = SomeArgs.obtain();
         args.argi1 = id;
         args.argi2 = vid;
@@ -188,7 +199,27 @@
         }
 
         // native callback
-        public void onDeviceOutput(byte[] data) {
+        public void onDeviceOutput(byte rtype, byte[] data) {
+            JSONObject json = new JSONObject();
+            try {
+                json.put("eventId", UHID_EVENT_TYPE_UHID_OUTPUT);
+                json.put("deviceId", mId);
+                json.put("reportType", rtype);
+                JSONArray dataArray = new JSONArray();
+                for (int i = 0; i < data.length; i++) {
+                    dataArray.put(data[i] & 0xFF);
+                }
+                json.put("reportData", dataArray);
+            } catch (JSONException e) {
+                throw new RuntimeException("Could not create JSON object ", e);
+            }
+            try {
+                mOutputStream.write(json.toString().getBytes());
+                mOutputStream.flush();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+
             if (mOutputs == null) {
                 Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found");
                 return;
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index dec4a56..5c08704 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -30,8 +30,9 @@
 
 #include <binder/ProcessState.h>
 
-#include <gui/SurfaceComposerClient.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
 
 #include <ui/DisplayInfo.h>
 #include <ui/GraphicTypes.h>
@@ -181,13 +182,18 @@
     ProcessState::self()->setThreadPoolMaxThreadCount(0);
     ProcessState::self()->startThreadPool();
 
-    ScreenCaptureResults captureResults;
-    status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults);
+    sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+    status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
     if (result != NO_ERROR) {
         close(fd);
         return 1;
     }
 
+    ScreenCaptureResults captureResults = captureListener->waitForResults();
+    if (captureResults.result != NO_ERROR) {
+        close(fd);
+        return 1;
+    }
     ui::Dataspace dataspace = captureResults.capturedDataspace;
     sp<GraphicBuffer> buffer = captureResults.buffer;
 
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 3b65f82..4574b2e 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -25,8 +25,9 @@
 using std::unordered_map;
 using std::vector;
 
-CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
-    : ConditionTracker(id, index) {
+CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index,
+                                                         const uint64_t protoHash)
+    : ConditionTracker(id, index, protoHash) {
     VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
 }
 
@@ -122,6 +123,49 @@
     return true;
 }
 
+bool CombinationConditionTracker::onConfigUpdated(
+        const vector<Predicate>& allConditionProtos, const int index,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& conditionTrackerMap) {
+    ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+                                      atomMatchingTrackerMap, conditionTrackerMap);
+    mTrackerIndex.clear();
+    mChildren.clear();
+    mUnSlicedChildren.clear();
+    mSlicedChildren.clear();
+    Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination();
+
+    for (const int64_t child : combinationCondition.predicate()) {
+        const auto& it = conditionTrackerMap.find(child);
+
+        if (it == conditionTrackerMap.end()) {
+            ALOGW("Predicate %lld not found in the config", (long long)child);
+            return false;
+        }
+
+        int childIndex = it->second;
+        const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex];
+
+        // Ensures that the child's tracker indices are updated.
+        if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers,
+                                           atomMatchingTrackerMap, conditionTrackerMap)) {
+            ALOGW("Child update failed %lld ", (long long)child);
+            return false;
+        }
+
+        if (allConditionTrackers[childIndex]->isSliced()) {
+            mSlicedChildren.push_back(childIndex);
+        } else {
+            mUnSlicedChildren.push_back(childIndex);
+        }
+        mChildren.push_back(childIndex);
+        mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
+                             childTracker->getAtomMatchingTrackerIndex().end());
+    }
+    return true;
+}
+
 void CombinationConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
         const bool isPartialLink,
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index a7fac3d..672d61c 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -24,9 +24,9 @@
 namespace os {
 namespace statsd {
 
-class CombinationConditionTracker : public virtual ConditionTracker {
+class CombinationConditionTracker : public ConditionTracker {
 public:
-    CombinationConditionTracker(const int64_t& id, const int index);
+    CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash);
 
     ~CombinationConditionTracker();
 
@@ -35,6 +35,11 @@
               const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
               std::vector<ConditionState>& conditionCache) override;
 
+    bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                         const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                         const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
+
     void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
@@ -102,6 +107,7 @@
     std::vector<int> mSlicedChildren;
     std::vector<int> mUnSlicedChildren;
 
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 4e12535..3bf4e63 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -31,18 +31,17 @@
 
 class ConditionTracker : public virtual RefBase {
 public:
-    ConditionTracker(const int64_t& id, const int index)
+    ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash)
         : mConditionId(id),
           mIndex(index),
           mInitialized(false),
           mTrackerIndex(),
           mUnSlicedPartCondition(ConditionState::kUnknown),
-          mSliced(false){};
+          mSliced(false),
+          mProtoHash(protoHash){};
 
     virtual ~ConditionTracker(){};
 
-    inline const int64_t& getId() { return mConditionId; }
-
     // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
     // be done in the constructor, but we do it separately because (1) easy to return a bool to
     // indicate whether the initialization is successful. (2) makes unit test easier.
@@ -50,7 +49,7 @@
     // fill the condition cache with the current condition.
     // allConditionConfig: the list of all Predicate config from statsd_config.
     // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
-    //                       need to call init() on children conditions)
+    //                       need to call init() on child conditions)
     // conditionIdIndexMap: the mapping from condition id to its index.
     // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
     // conditionCache: tracks initial conditions of all ConditionTrackers. returns the
@@ -60,6 +59,26 @@
                       const std::unordered_map<int64_t, int>& conditionIdIndexMap,
                       std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0;
 
+    // Update appropriate state on config updates. Primarily, all indices need to be updated.
+    // This predicate and all of its children are guaranteed to be preserved across the update.
+    // This function is recursive and will call onConfigUpdated on child conditions. It does not
+    // manage cycle detection since all preserved conditions should not have any cycles.
+    //
+    // allConditionProtos: the new predicates.
+    // index: the new index of this tracker in allConditionProtos and allConditionTrackers.
+    // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
+    //                       need to call onConfigUpdated() on child conditions)
+    // [atomMatchingTrackerMap]: map of atom matcher id to index after the config update
+    // [conditionTrackerMap]: map of condition tracker id to index after the config update.
+    // returns whether or not the update is successful
+    virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                                 const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                                 const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                                 const std::unordered_map<int64_t, int>& conditionTrackerMap) {
+        mIndex = index;
+        return true;
+    }
+
     // evaluate current condition given the new event.
     // event: the new log event
     // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always
@@ -112,6 +131,10 @@
         return mConditionId;
     }
 
+    inline uint64_t getProtoHash() const {
+        return mProtoHash;
+    }
+
     virtual void getTrueSlicedDimensions(
         const std::vector<sp<ConditionTracker>>& allConditions,
         std::set<HashableDimensionKey>* dimensions) const = 0;
@@ -133,7 +156,7 @@
     const int64_t mConditionId;
 
     // the index of this condition in the manager's condition list.
-    const int mIndex;
+    int mIndex;
 
     // if it's properly initialized.
     bool mInitialized;
@@ -151,6 +174,12 @@
     ConditionState mUnSlicedPartCondition;
 
     bool mSliced;
+
+    // Hash of the Predicate's proto bytes from StatsdConfig.
+    // Used to determine if the definition of this condition has changed across a config update.
+    const uint64_t mProtoHash;
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index f45759b..1dcc8f9 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -27,54 +27,21 @@
 using std::unordered_map;
 
 SimpleConditionTracker::SimpleConditionTracker(
-        const ConfigKey& key, const int64_t& id, const int index,
+        const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index,
         const SimplePredicate& simplePredicate,
-        const unordered_map<int64_t, int>& trackerNameIndexMap)
-    : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) {
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap)
+    : ConditionTracker(id, index, protoHash),
+      mConfigKey(key),
+      mContainANYPositionInInternalDimensions(false) {
     VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
     mCountNesting = simplePredicate.count_nesting();
 
-    if (simplePredicate.has_start()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.start());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
-            return;
-        }
-        mStartLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStartLogMatcherIndex);
-    } else {
-        mStartLogMatcherIndex = -1;
-    }
-
-    if (simplePredicate.has_stop()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.stop());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
-            return;
-        }
-        mStopLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStopLogMatcherIndex);
-    } else {
-        mStopLogMatcherIndex = -1;
-    }
-
-    if (simplePredicate.has_stop_all()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
-            return;
-        }
-        mStopAllLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStopAllLogMatcherIndex);
-    } else {
-        mStopAllLogMatcherIndex = -1;
-    }
+    setMatcherIndices(simplePredicate, atomMatchingTrackerMap);
 
     if (simplePredicate.has_dimensions()) {
         translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
         if (mOutputDimensions.size() > 0) {
             mSliced = true;
-            mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
         }
         mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
     }
@@ -106,6 +73,59 @@
     return mInitialized;
 }
 
+bool SimpleConditionTracker::onConfigUpdated(
+        const vector<Predicate>& allConditionProtos, const int index,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& conditionTrackerMap) {
+    ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+                                      atomMatchingTrackerMap, conditionTrackerMap);
+    setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap);
+    return true;
+}
+
+void SimpleConditionTracker::setMatcherIndices(
+        const SimplePredicate& simplePredicate,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    mTrackerIndex.clear();
+    if (simplePredicate.has_start()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.start());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
+            return;
+        }
+        mStartLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStartLogMatcherIndex);
+    } else {
+        mStartLogMatcherIndex = -1;
+    }
+
+    if (simplePredicate.has_stop()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.stop());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
+            return;
+        }
+        mStopLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopLogMatcherIndex);
+    } else {
+        mStopLogMatcherIndex = -1;
+    }
+
+    if (simplePredicate.has_stop_all()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.stop_all());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Stop all matcher %lld found in the config",
+                  (long long)simplePredicate.stop_all());
+            return;
+        }
+        mStopAllLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopAllLogMatcherIndex);
+    } else {
+        mStopAllLogMatcherIndex = -1;
+    }
+}
+
 void SimpleConditionTracker::dumpState() {
     VLOG("%lld DUMP:", (long long)mConditionId);
     for (const auto& pair : mSlicedConditionState) {
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 1a9e35e..7a8b4010 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -27,11 +27,11 @@
 namespace os {
 namespace statsd {
 
-class SimpleConditionTracker : public virtual ConditionTracker {
+class SimpleConditionTracker : public ConditionTracker {
 public:
-    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
-                           const SimplePredicate& simplePredicate,
-                           const std::unordered_map<int64_t, int>& trackerNameIndexMap);
+    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash,
+                           const int index, const SimplePredicate& simplePredicate,
+                           const std::unordered_map<int64_t, int>& atomMatchingTrackerMap);
 
     ~SimpleConditionTracker();
 
@@ -40,6 +40,11 @@
               const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
               std::vector<ConditionState>& conditionCache) override;
 
+    bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                         const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                         const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
+
     void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
@@ -112,10 +117,11 @@
     std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
     std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
 
-    int mDimensionTag;
-
     std::map<HashableDimensionKey, int> mSlicedConditionState;
 
+    void setMatcherIndices(const SimplePredicate& predicate,
+                           const std::unordered_map<int64_t, int>& logTrackerMap);
+
     void handleStopAll(std::vector<ConditionState>& conditionCache,
                        std::vector<bool>& changedCache);
 
@@ -129,6 +135,7 @@
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
     FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5739612..3dbb6ed 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -69,13 +69,14 @@
 CountMetricProducer::CountMetricProducer(
         const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-        const int64_t timeBaseNs, const int64_t startTimeNs,
+        const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
+                     protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+                     stateGroupMap) {
     if (metric.has_bucket()) {
         mBucketSizeNs =
                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index f05fb06..6b2f2ca 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -44,7 +44,7 @@
     CountMetricProducer(
             const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                     eventDeactivationMap = {},
@@ -57,6 +57,10 @@
                         const HashableDimensionKey& primaryKey, const FieldValue& oldState,
                         const FieldValue& newState) override;
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_COUNT;
+    }
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index e9b0438..3acafaa 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -66,14 +66,15 @@
         const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache, const size_t startIndex,
         const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
-        const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
-        const int64_t timeBaseNs, const int64_t startTimeNs,
+        const sp<ConditionWizard>& wizard, const uint64_t protoHash,
+        const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
+                     protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+                     stateGroupMap),
       mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index bfe1010..3a94d9c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -42,8 +42,9 @@
             const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const size_t startIndex,
             const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
-            const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
-            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const sp<ConditionWizard>& wizard, const uint64_t protoHash,
+            const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+            const int64_t startTimeNs,
             const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
             const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
             const vector<int>& slicedStateAtoms = {},
@@ -58,6 +59,10 @@
                         const HashableDimensionKey& primaryKey, const FieldValue& oldState,
                         const FieldValue& newState) override;
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_DURATION;
+    }
+
 protected:
     void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index dc0036a..dfe4559 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -55,13 +55,14 @@
 EventMetricProducer::EventMetricProducer(
         const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-        const int64_t startTimeNs,
+        const uint64_t protoHash, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
+                     protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+                     stateGroupMap) {
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
             Metric2Condition mc;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index bfb2de3..e828ddd 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -36,7 +36,7 @@
     EventMetricProducer(
             const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-            const int64_t startTimeNs,
+            const uint64_t protoHash, const int64_t startTimeNs,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                     eventDeactivationMap = {},
@@ -45,6 +45,10 @@
 
     virtual ~EventMetricProducer();
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_EVENT;
+    }
+
 private:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 020f4b6..9dda248 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -71,13 +71,14 @@
 GaugeMetricProducer::GaugeMetricProducer(
         const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-        const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
-        const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
-        const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const uint64_t protoHash, const int whatMatcherIndex,
+        const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
+        const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
+        const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
+                     protoHash, eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
                      /*stateGroupMap=*/{}),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index ef3a24a..e933d4b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -59,10 +59,11 @@
     GaugeMetricProducer(
             const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache,
-            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
-            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
-            const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
-            const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+            const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+            const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+            const int pullTagId, const int triggerAtomId, const int atomId,
+            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const sp<StatsPullerManager>& pullerManager,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                     eventDeactivationMap = {});
@@ -96,6 +97,10 @@
         }
     };
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_GAUGE;
+    }
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index fe143e4..c1c1d20f 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -46,13 +46,14 @@
 MetricProducer::MetricProducer(
         const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
         const int conditionIndex, const vector<ConditionState>& initialConditionCache,
-        const sp<ConditionWizard>& wizard,
+        const sp<ConditionWizard>& wizard, const uint64_t protoHash,
         const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
         const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                 eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : mMetricId(metricId),
+      mProtoHash(protoHash),
       mConfigKey(key),
       mTimeBaseNs(timeBaseNs),
       mCurrentBucketStartTimeNs(timeBaseNs),
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index be4cd67..bb590aa 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -87,6 +87,13 @@
     NO_DATA = 9
 };
 
+enum MetricType {
+    METRIC_TYPE_EVENT = 1,
+    METRIC_TYPE_COUNT = 2,
+    METRIC_TYPE_DURATION = 3,
+    METRIC_TYPE_GAUGE = 4,
+    METRIC_TYPE_VALUE = 5,
+};
 struct Activation {
     Activation(const ActivationType& activationType, const int64_t ttlNs)
         : ttl_ns(ttlNs),
@@ -130,7 +137,7 @@
 public:
     MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
                    const int conditionIndex, const vector<ConditionState>& initialConditionCache,
-                   const sp<ConditionWizard>& wizard,
+                   const sp<ConditionWizard>& wizard, const uint64_t protoHash,
                    const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
                    const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                            eventDeactivationMap,
@@ -259,10 +266,16 @@
             int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
 
     // Start: getters/setters
-    inline const int64_t& getMetricId() const {
+    inline int64_t getMetricId() const {
         return mMetricId;
     }
 
+    inline uint64_t getProtoHash() const {
+        return mProtoHash;
+    }
+
+    virtual MetricType getMetricType() const = 0;
+
     // For test only.
     inline int64_t getCurrentBucketNum() const {
         return mCurrentBucketNum;
@@ -400,6 +413,10 @@
 
     const int64_t mMetricId;
 
+    // Hash of the Metric's proto bytes from StatsdConfig, including any activations.
+    // Used to determine if the definition of this metric has changed across a config update.
+    const uint64_t mProtoHash;
+
     const ConfigKey mConfigKey;
 
     // The time when this metric producer was first created. The end time for the current bucket
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 5a52032..3980689 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -80,11 +80,11 @@
     mConfigValid = initStatsdConfig(
             key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
             timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
-            mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
-            mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
-            mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
+            mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
+            mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap,
+            mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
             mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation,
-            mNoReportMetricIds);
+            mStateProtoHashes, mNoReportMetricIds);
 
     mHashStringsInReport = config.hash_strings_in_metric_report();
     mVersionStringsInReport = config.version_strings_in_metric_report();
@@ -204,13 +204,20 @@
                                   const sp<AlarmMonitor>& periodicAlarmMonitor) {
     vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
     unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> newConditionTrackers;
+    unordered_map<int64_t, int> newConditionTrackerMap;
     mTagIds.clear();
+    mTrackerToConditionMap.clear();
     mConfigValid = updateStatsdConfig(
             mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, mTagIds,
-            newAtomMatchingTrackers, newAtomMatchingTrackerMap);
+            timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
+            mAllConditionTrackers, mConditionTrackerMap, mTagIds, newAtomMatchingTrackers,
+            newAtomMatchingTrackerMap, newConditionTrackers, newConditionTrackerMap,
+            mTrackerToConditionMap);
     mAllAtomMatchingTrackers = newAtomMatchingTrackers;
     mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
+    mAllConditionTrackers = newConditionTrackers;
+    mConditionTrackerMap = newConditionTrackerMap;
     return mConfigValid;
 }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6f4b2d7..27f3d51 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -242,9 +242,15 @@
     // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and
     // ConditionTracker to its index in the corresponding vector.
 
-    // Maps the id of an atom matcher to its index in mAllAtomMatchingTrackers.
+    // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers.
     std::unordered_map<int64_t, int> mAtomMatchingTrackerMap;
 
+    // Maps the id of a condition tracker to its index in mAllConditionTrackers.
+    std::unordered_map<int64_t, int> mConditionTrackerMap;
+
+    // Maps the id of a metric producer to its index in mAllMetricProducers.
+    std::unordered_map<int64_t, int> mMetricProducerMap;
+
     // To make the log processing more efficient, we want to do as much filtering as possible
     // before we go into individual trackers and conditions to match.
 
@@ -292,6 +298,9 @@
     // The config is always active if any metric in the config does not have an activation signal.
     bool mIsAlwaysActive;
 
+    // Hashes of the States used in this config, keyed by the state id, used in config updates.
+    std::map<int64_t, uint64_t> mStateProtoHashes;
+
     FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 9b684f1..39ae9a4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -79,16 +79,17 @@
 ValueMetricProducer::ValueMetricProducer(
         const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache,
-        const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
-        const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
-        const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+        const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
+        const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache,
-                     conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
-                     stateGroupMap),
+                     conditionWizard, protoHash, eventActivationMap, eventDeactivationMap,
+                     slicedStateAtoms, stateGroupMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
@@ -411,7 +412,7 @@
 
 void ValueMetricProducer::resetBase() {
     for (auto& slice : mCurrentBaseInfo) {
-        for (auto& baseInfo : slice.second) {
+        for (auto& baseInfo : slice.second.baseInfos) {
             baseInfo.hasBase = false;
         }
     }
@@ -623,7 +624,7 @@
                 mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
         if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
             auto it = mCurrentBaseInfo.find(whatKey);
-            for (auto& baseInfo : it->second) {
+            for (auto& baseInfo : it->second.baseInfos) {
                 baseInfo.hasBase = false;
             }
         }
@@ -652,7 +653,7 @@
             (unsigned long)mCurrentSlicedBucket.size());
     if (verbose) {
         for (const auto& it : mCurrentSlicedBucket) {
-          for (const auto& interval : it.second) {
+          for (const auto& interval : it.second.intervals) {
               fprintf(out, "\t(what)%s\t(states)%s  (value)%s\n",
                       it.first.getDimensionKeyInWhat().toString().c_str(),
                       it.first.getStateValuesKey().toString().c_str(),
@@ -788,23 +789,23 @@
         return;
     }
 
-    vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
+    DimensionsInWhatInfo& dimensionsInWhatInfo = mCurrentBaseInfo[whatKey];
+    vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
     if (baseInfos.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
         baseInfos.resize(mFieldMatchers.size());
     }
 
-    for (BaseInfo& baseInfo : baseInfos) {
-        if (!baseInfo.hasCurrentState) {
-            baseInfo.currentState = getUnknownStateKey();
-            baseInfo.hasCurrentState = true;
-        }
+    if (!dimensionsInWhatInfo.hasCurrentState) {
+        dimensionsInWhatInfo.currentState = getUnknownStateKey();
+        dimensionsInWhatInfo.hasCurrentState = true;
     }
 
     // We need to get the intervals stored with the previous state key so we can
     // close these value intervals.
-    const auto oldStateKey = baseInfos[0].currentState;
-    vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+    const auto oldStateKey = dimensionsInWhatInfo.currentState;
+    vector<Interval>& intervals =
+            mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
     if (intervals.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
         intervals.resize(mFieldMatchers.size());
@@ -818,14 +819,14 @@
     // Discussion here: http://ag/6124370.
     bool useAnomalyDetection = true;
 
+    dimensionsInWhatInfo.hasCurrentState = true;
+    dimensionsInWhatInfo.currentState = stateKey;
     for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
         const Matcher& matcher = mFieldMatchers[i];
         BaseInfo& baseInfo = baseInfos[i];
         Interval& interval = intervals[i];
         interval.valueIndex = i;
         Value value;
-        baseInfo.hasCurrentState = true;
-        baseInfo.currentState = stateKey;
         if (!getDoubleOrLong(event, matcher, value)) {
             VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
             StatsdStats::getInstance().noteBadValueType(mMetricId);
@@ -990,7 +991,7 @@
         bool bucketHasData = false;
         // The current bucket is large enough to keep.
         for (const auto& slice : mCurrentSlicedBucket) {
-            ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
+            PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals);
             bucket.mConditionTrueNs = conditionTrueDuration;
             // it will auto create new vector of ValuebucketInfo if the key is not found.
             if (bucket.valueIndex.size() > 0) {
@@ -1030,9 +1031,9 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
-ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
-                                                    const std::vector<Interval>& intervals) {
-    ValueBucket bucket;
+PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
+                                                        const std::vector<Interval>& intervals) {
+    PastValueBucket bucket;
     bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
     bucket.mBucketEndNs = bucketEndTime;
     for (const auto& interval : intervals) {
@@ -1059,7 +1060,7 @@
     // Cleanup data structure to aggregate values.
     for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
         bool obsolete = true;
-        for (auto& interval : it->second) {
+        for (auto& interval : it->second.intervals) {
             interval.hasValue = false;
             interval.sampleSize = 0;
             if (interval.seenNewData) {
@@ -1107,7 +1108,7 @@
                     continue;
                 }
                 // TODO: fix this when anomaly can accept double values
-                auto& interval = slice.second[0];
+                auto& interval = slice.second.intervals[0];
                 if (interval.hasValue) {
                     mCurrentFullBucket[slice.first] += interval.value.long_value;
                 }
@@ -1126,7 +1127,7 @@
                 for (auto& tracker : mAnomalyTrackers) {
                     if (tracker != nullptr) {
                         // TODO: fix this when anomaly can accept double values
-                        auto& interval = slice.second[0];
+                        auto& interval = slice.second.intervals[0];
                         if (interval.hasValue) {
                             tracker->addPastBucket(slice.first, interval.value.long_value,
                                                    mCurrentBucketNum);
@@ -1139,7 +1140,7 @@
         // Accumulate partial bucket.
         for (const auto& slice : mCurrentSlicedBucket) {
             // TODO: fix this when anomaly can accept double values
-            auto& interval = slice.second[0];
+            auto& interval = slice.second.intervals[0];
             if (interval.hasValue) {
                 mCurrentFullBucket[slice.first] += interval.value.long_value;
             }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index e72002e..4b2599b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -31,7 +31,7 @@
 namespace os {
 namespace statsd {
 
-struct ValueBucket {
+struct PastValueBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
     std::vector<int> valueIndex;
@@ -41,7 +41,6 @@
     int64_t mConditionTrueNs;
 };
 
-
 // Aggregates values within buckets.
 //
 // There are different events that might complete a bucket
@@ -53,9 +52,9 @@
     ValueMetricProducer(
             const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache,
-            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
-            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
-            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+            const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+            const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
             const sp<StatsPullerManager>& pullerManager,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
@@ -93,6 +92,10 @@
     void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
                         const FieldValue& oldState, const FieldValue& newState) override;
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_VALUE;
+    }
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -173,7 +176,7 @@
     // if this is pulled metric
     const bool mIsPulled;
 
-    // internal state of an ongoing aggregation bucket.
+    // Tracks the value information of one value field.
     typedef struct {
         // Index in multi value aggregation.
         int valueIndex;
@@ -188,25 +191,40 @@
         bool seenNewData = false;
     } Interval;
 
+    // Internal state of an ongoing aggregation bucket.
+    typedef struct CurrentValueBucket {
+        // Value information for each value field of the metric.
+        std::vector<Interval> intervals;
+    } CurrentValueBucket;
+
+    // Holds base information for diffing values from one value field.
     typedef struct {
         // Holds current base value of the dimension. Take diff and update if necessary.
         Value base;
         // Whether there is a base to diff to.
         bool hasBase;
+    } BaseInfo;
+
+    // State key and base information for a specific DimensionsInWhat key.
+    typedef struct {
+        std::vector<BaseInfo> baseInfos;
         // Last seen state value(s).
         HashableDimensionKey currentState;
         // Whether this dimensions in what key has a current state key.
         bool hasCurrentState;
-    } BaseInfo;
+    } DimensionsInWhatInfo;
 
-    std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
+    // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat
+    // key and StateValuesKey pair.
+    std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket;
 
-    std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
+    // Tracks current state key and base information for each DimensionsInWhat key.
+    std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mCurrentBaseInfo;
 
     std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
-    std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets;
 
     const int64_t mMinBucketSizeNs;
 
@@ -224,8 +242,8 @@
     void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
 
-    ValueBucket buildPartialBucket(int64_t bucketEndTime,
-                                   const std::vector<Interval>& intervals);
+    PastValueBucket buildPartialBucket(int64_t bucketEndTime,
+                                       const std::vector<Interval>& intervals);
 
     void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
 
@@ -234,7 +252,7 @@
     // Reset diff base and mHasGlobalBase
     void resetBase();
 
-    static const size_t kBucketSize = sizeof(ValueBucket{});
+    static const size_t kBucketSize = sizeof(PastValueBucket{});
 
     const size_t mDimensionSoftLimit;
 
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 0983dc0..bd60b6b 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -15,6 +15,7 @@
  */
 
 #define DEBUG false  // STOPSHIP if true
+#include "Log.h"
 
 #include "config_update_utils.h"
 
@@ -44,7 +45,7 @@
     // Check if new matcher.
     const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
     if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
-        matchersToUpdate[matcherIdx] = UPDATE_REPLACE;
+        matchersToUpdate[matcherIdx] = UPDATE_NEW;
         return true;
     }
 
@@ -103,11 +104,13 @@
     return true;
 }
 
-bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
-                        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
-                        const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                        set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
-                        vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers) {
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                                const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                                const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                                set<int>& allTagIds,
+                                unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                                vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                                set<int64_t>& replacedMatchers) {
     const int atomMatcherCount = config.atom_matcher_size();
 
     vector<AtomMatcher> matcherProtos;
@@ -157,7 +160,10 @@
                 newAtomMatchingTrackers.push_back(tracker);
                 break;
             }
-            case UPDATE_REPLACE: {
+            case UPDATE_REPLACE:
+                replacedMatchers.insert(id);
+                [[fallthrough]];  // Intentionally fallthrough to create the new matcher.
+            case UPDATE_NEW: {
                 sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap);
                 if (tracker == nullptr) {
                     return false;
@@ -187,6 +193,207 @@
     return true;
 }
 
+// Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+                                    const unordered_map<int64_t, int>& oldConditionTrackerMap,
+                                    const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                                    const unordered_map<int64_t, int>& newConditionTrackerMap,
+                                    const set<int64_t>& replacedMatchers,
+                                    vector<UpdateStatus>& conditionsToUpdate,
+                                    vector<bool>& cycleTracker) {
+    // Have already examined this condition.
+    if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) {
+        return true;
+    }
+
+    const Predicate& predicate = config.predicate(conditionIdx);
+    int64_t id = predicate.id();
+    // Check if new condition.
+    const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+    if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+        conditionsToUpdate[conditionIdx] = UPDATE_NEW;
+        return true;
+    }
+
+    // This is an existing condition. Check if it has changed.
+    string serializedCondition;
+    if (!predicate.SerializeToString(&serializedCondition)) {
+        ALOGE("Unable to serialize matcher %lld", (long long)id);
+        return false;
+    }
+    uint64_t newProtoHash = Hash64(serializedCondition);
+    if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) {
+        conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+        return true;
+    }
+
+    switch (predicate.contents_case()) {
+        case Predicate::ContentsCase::kSimplePredicate: {
+            // Need to check if any of the underlying matchers changed.
+            const SimplePredicate& simplePredicate = predicate.simple_predicate();
+            if (simplePredicate.has_start()) {
+                if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            if (simplePredicate.has_stop()) {
+                if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            if (simplePredicate.has_stop_all()) {
+                if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE;
+            return true;
+        }
+        case Predicate::ContentsCase::kCombination: {
+            // Need to recurse on the children to see if any of the child predicates changed.
+            cycleTracker[conditionIdx] = true;
+            UpdateStatus status = UPDATE_PRESERVE;
+            for (const int64_t childPredicateId : predicate.combination().predicate()) {
+                const auto& childIt = newConditionTrackerMap.find(childPredicateId);
+                if (childIt == newConditionTrackerMap.end()) {
+                    ALOGW("Predicate %lld not found in the config", (long long)childPredicateId);
+                    return false;
+                }
+                const int childIdx = childIt->second;
+                if (cycleTracker[childIdx]) {
+                    ALOGE("Cycle detected in predicate config");
+                    return false;
+                }
+                if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap,
+                                                    oldConditionTrackers, newConditionTrackerMap,
+                                                    replacedMatchers, conditionsToUpdate,
+                                                    cycleTracker)) {
+                    return false;
+                }
+
+                if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) {
+                    status = UPDATE_REPLACE;
+                    break;
+                }
+            }
+            conditionsToUpdate[conditionIdx] = status;
+            cycleTracker[conditionIdx] = false;
+            return true;
+        }
+        default: {
+            ALOGE("Predicate \"%lld\" malformed", (long long)id);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+                      const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      const set<int64_t>& replacedMatchers,
+                      const unordered_map<int64_t, int>& oldConditionTrackerMap,
+                      const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                      unordered_map<int64_t, int>& newConditionTrackerMap,
+                      vector<sp<ConditionTracker>>& newConditionTrackers,
+                      unordered_map<int, vector<int>>& trackerToConditionMap,
+                      vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) {
+    vector<Predicate> conditionProtos;
+    const int conditionTrackerCount = config.predicate_size();
+    conditionProtos.reserve(conditionTrackerCount);
+    newConditionTrackers.reserve(conditionTrackerCount);
+    conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
+
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        const Predicate& condition = config.predicate(i);
+        if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) {
+            ALOGE("Duplicate Predicate found!");
+            return false;
+        }
+        newConditionTrackerMap[condition.id()] = i;
+        conditionProtos.push_back(condition);
+    }
+
+    vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(conditionTrackerCount, false);
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers,
+                                            newConditionTrackerMap, replacedMatchers,
+                                            conditionsToUpdate, cycleTracker)) {
+            return false;
+        }
+    }
+
+    // Update status has been determined for all conditions. Now perform the update.
+    set<int> preservedConditions;
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        const Predicate& predicate = config.predicate(i);
+        const int64_t id = predicate.id();
+        switch (conditionsToUpdate[i]) {
+            case UPDATE_PRESERVE: {
+                preservedConditions.insert(i);
+                const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+                if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+                    ALOGE("Could not find Predicate %lld in the previous config, but expected it "
+                          "to be there",
+                          (long long)id);
+                    return false;
+                }
+                const int oldIndex = oldConditionTrackerIt->second;
+                newConditionTrackers.push_back(oldConditionTrackers[oldIndex]);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedConditions.insert(id);
+                [[fallthrough]];  // Intentionally fallthrough to create the new condition tracker.
+            case UPDATE_NEW: {
+                sp<ConditionTracker> tracker =
+                        createConditionTracker(key, predicate, i, atomMatchingTrackerMap);
+                if (tracker == nullptr) {
+                    return false;
+                }
+                newConditionTrackers.push_back(tracker);
+                break;
+            }
+            default: {
+                ALOGE("Condition \"%lld\" update state is unknown. This should never happen",
+                      (long long)id);
+                return false;
+            }
+        }
+    }
+
+    // Update indices of preserved predicates.
+    for (const int conditionIndex : preservedConditions) {
+        if (!newConditionTrackers[conditionIndex]->onConfigUpdated(
+                    conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap,
+                    newConditionTrackerMap)) {
+            ALOGE("Failed to update condition %lld",
+                  (long long)newConditionTrackers[conditionIndex]->getConditionId());
+            return false;
+        }
+    }
+
+    std::fill(cycleTracker.begin(), cycleTracker.end(), false);
+    for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) {
+        const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex];
+        // Calling init on preserved conditions is OK. It is needed to fill the condition cache.
+        if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap,
+                                    cycleTracker, conditionCache)) {
+            return false;
+        }
+        for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
+            vector<int>& conditionList = trackerToConditionMap[trackerIndex];
+            conditionList.push_back(conditionIndex);
+        }
+    }
+    return true;
+}
+
 bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
                         const sp<StatsPullerManager>& pullerManager,
                         const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -194,14 +401,34 @@
                         const int64_t currentTimeNs,
                         const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
                         const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                        const unordered_map<int64_t, int>& oldConditionTrackerMap,
                         set<int>& allTagIds,
                         vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
-                        unordered_map<int64_t, int>& newAtomMatchingTrackerMap) {
-    if (!updateAtomTrackers(config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers,
-                            allTagIds, newAtomMatchingTrackerMap, newAtomMatchingTrackers)) {
+                        unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                        vector<sp<ConditionTracker>>& newConditionTrackers,
+                        unordered_map<int64_t, int>& newConditionTrackerMap,
+                        unordered_map<int, vector<int>>& trackerToConditionMap) {
+    set<int64_t> replacedMatchers;
+    set<int64_t> replacedConditions;
+    vector<ConditionState> conditionCache;
+
+    if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap,
+                                    oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap,
+                                    newAtomMatchingTrackers, replacedMatchers)) {
         ALOGE("updateAtomMatchingTrackers failed");
         return false;
     }
+    VLOG("updateAtomMatchingTrackers succeeded");
+
+    if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers,
+                          oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap,
+                          newConditionTrackers, trackerToConditionMap, conditionCache,
+                          replacedConditions)) {
+        ALOGE("updateConditions failed");
+        return false;
+    }
+    VLOG("updateConditions succeeded");
 
     return true;
 }
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
index ae7b216..7ba684a 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include "anomaly/AlarmMonitor.h"
+#include "condition/ConditionTracker.h"
 #include "external/StatsPullerManager.h"
 #include "matchers/AtomMatchingTracker.h"
 
@@ -31,30 +32,33 @@
 // All other functions are intermediate steps, created to make unit testing easier.
 
 // Possible update states for a component. PRESERVE means we should keep the existing one.
-// REPLACE means we should create a new one, either because it didn't exist or it changed.
+// REPLACE means we should create a new one because the existing one changed
+// NEW means we should create a new one because one does not currently exist.
 enum UpdateStatus {
     UPDATE_UNKNOWN = 0,
     UPDATE_PRESERVE = 1,
     UPDATE_REPLACE = 2,
+    UPDATE_NEW = 3,
 };
 
 // Recursive function to determine if a matcher needs to be updated.
 // input:
 // [config]: the input StatsdConfig
 // [matcherIdx]: the index of the current matcher to be updated
-// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig
 // [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager
 // [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
+// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig
 // output:
 // [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will
 //                     be updated from UPDATE_UNKNOWN after this call.
 // [cycleTracker]: intermediate param used during recursion.
-bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx,
-                                  const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
-                                  const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                                  const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
-                                  vector<UpdateStatus>& matchersToUpdate,
-                                  vector<bool>& cycleTracker);
+// Returns whether the function was successful or not.
+bool determineMatcherUpdateStatus(
+        const StatsdConfig& config, const int matcherIdx,
+        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker);
 
 // Updates the AtomMatchingTrackers.
 // input:
@@ -64,12 +68,61 @@
 // output:
 // [allTagIds]: contains the set of all interesting tag ids to this config.
 // [newAtomMatchingTrackerMap]: new matcher id to index mapping
-// [newAtomMatchers]: stores the new AtomMatchingTrackers
-bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
-                        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
-                        const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                        set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
-                        vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers);
+// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers
+// [replacedMatchers]: set of matcher ids that changed and have been replaced
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                                const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                                const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                                std::set<int>& allTagIds,
+                                std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                                std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                                std::set<int64_t>& replacedMatchers);
+
+// Recursive function to determine if a condition needs to be updated.
+// input:
+// [config]: the input StatsdConfig
+// [conditionIdx]: the index of the current condition to be updated
+// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// [newConditionTrackerMap]: condition id to index mapping in the input StatsdConfig
+// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced
+// output:
+// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will
+//                       be updated from UPDATE_UNKNOWN after this call.
+// [cycleTracker]: intermediate param used during recursion.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+                                    const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+                                    const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                                    const std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                                    const std::set<int64_t>& replacedMatchers,
+                                    std::vector<UpdateStatus>& conditionsToUpdate,
+                                    std::vector<bool>& cycleTracker);
+
+// Updates ConditionTrackers
+// input:
+// [config]: the input config
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced
+// [oldConditionTrackerMap]: existing matcher id to index mapping
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// output:
+// [newConditionTrackerMap]: new condition id to index mapping
+// [newConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contains the mapping from the index of an atom matcher
+//                          to indices of condition trackers that use the matcher
+// [conditionCache]: stores the current conditions for each ConditionTracker
+// [replacedConditions]: set of matcher ids that have changed and have been replaced
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+                      const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      const std::set<int64_t>& replacedMatchers,
+                      const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+                      const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                      std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                      std::vector<sp<ConditionTracker>>& newConditionTrackers,
+                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      std::vector<ConditionState>& conditionCache,
+                      std::set<int64_t>& replacedConditions);
 
 // Updates the existing MetricsManager from a new StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
@@ -79,10 +132,15 @@
                         const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
                         const int64_t currentTimeNs,
                         const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                        const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
                         std::set<int>& allTagIds,
                         std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
-                        unordered_map<int64_t, int>& newAtomMatchingTrackerMap);
+                        std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                        std::vector<sp<ConditionTracker>>& newConditionTrackers,
+                        std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                        std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index e40fbdb..3f40c90 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -38,6 +38,7 @@
 #include "state/StateManager.h"
 #include "stats_util.h"
 
+using google::protobuf::MessageLite;
 using std::set;
 using std::unordered_map;
 using std::vector;
@@ -60,6 +61,20 @@
     return true;
 }
 
+bool getMetricProtoHash(const MessageLite& metric, const int64_t id, const bool hasActivation,
+                        const uint64_t activationHash, uint64_t& metricHash) {
+    string serializedMetric;
+    if (!metric.SerializeToString(&serializedMetric)) {
+        ALOGE("Unable to serialize metric %lld", (long long)id);
+        return false;
+    }
+    metricHash = Hash64(serializedMetric);
+    if (hasActivation) {
+        metricHash = Hash64(to_string(metricHash).append(to_string(activationHash)));
+    }
+    return true;
+}
+
 }  // namespace
 
 sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
@@ -74,16 +89,37 @@
         case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
             return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash,
                                                  logMatcher.simple_atom_matcher(), uidMap);
-            break;
         case AtomMatcher::ContentsCase::kCombination:
             return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash);
-            break;
         default:
             ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
             return nullptr;
     }
 }
 
+sp<ConditionTracker> createConditionTracker(
+        const ConfigKey& key, const Predicate& predicate, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    string serializedPredicate;
+    if (!predicate.SerializeToString(&serializedPredicate)) {
+        ALOGE("Unable to serialize predicate %lld", (long long)predicate.id());
+        return nullptr;
+    }
+    uint64_t protoHash = Hash64(serializedPredicate);
+    switch (predicate.contents_case()) {
+        case Predicate::ContentsCase::kSimplePredicate: {
+            return new SimpleConditionTracker(key, predicate.id(), protoHash, index,
+                                              predicate.simple_predicate(), atomMatchingTrackerMap);
+        }
+        case Predicate::ContentsCase::kCombination: {
+            return new CombinationConditionTracker(predicate.id(), index, protoHash);
+        }
+        default:
+            ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id());
+            return nullptr;
+    }
+}
+
 bool handleMetricWithAtomMatchingTrackers(
         const int64_t what, const int metricIndex, const bool usedForDimension,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -207,19 +243,31 @@
 bool handleMetricActivation(
         const StatsdConfig& config, const int64_t metricId, const int metricIndex,
         const unordered_map<int64_t, int>& metricToActivationMap,
-        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap, bool& hasActivation,
         unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
         unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
         vector<int>& metricsWithActivation,
         unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
+        unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        uint64_t& activationHash) {
     // Check if metric has an associated activation
     auto itr = metricToActivationMap.find(metricId);
-    if (itr == metricToActivationMap.end()) return true;
+    if (itr == metricToActivationMap.end()) {
+        hasActivation = false;
+        return true;
+    }
 
+    hasActivation = true;
     int activationIndex = itr->second;
     const MetricActivation& metricActivation = config.metric_activation(activationIndex);
 
+    string serializedActivation;
+    if (!metricActivation.SerializeToString(&serializedActivation)) {
+        ALOGE("Unable to serialize metric activation for metric %lld", (long long)metricId);
+        return false;
+    }
+    activationHash = Hash64(serializedActivation);
+
     for (int i = 0; i < metricActivation.event_activation_size(); i++) {
         const EventActivation& activation = metricActivation.event_activation(i);
 
@@ -266,8 +314,7 @@
 
     for (int i = 0; i < atomMatcherCount; i++) {
         const AtomMatcher& logMatcher = config.atom_matcher(i);
-        int index = allAtomMatchingTrackers.size();
-        sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, index, uidMap);
+        sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap);
         if (tracker == nullptr) {
             return false;
         }
@@ -276,7 +323,7 @@
             ALOGE("Duplicate AtomMatcher found!");
             return false;
         }
-        atomMatchingTrackerMap[logMatcher.id()] = index;
+        atomMatchingTrackerMap[logMatcher.id()] = i;
         matcherConfigs.push_back(logMatcher);
     }
 
@@ -307,28 +354,17 @@
 
     for (int i = 0; i < conditionTrackerCount; i++) {
         const Predicate& condition = config.predicate(i);
-        int index = allConditionTrackers.size();
-        switch (condition.contents_case()) {
-            case Predicate::ContentsCase::kSimplePredicate: {
-                allConditionTrackers.push_back(new SimpleConditionTracker(
-                        key, condition.id(), index, condition.simple_predicate(),
-                        atomMatchingTrackerMap));
-                break;
-            }
-            case Predicate::ContentsCase::kCombination: {
-                allConditionTrackers.push_back(
-                        new CombinationConditionTracker(condition.id(), index));
-                break;
-            }
-            default:
-                ALOGE("Predicate \"%lld\" malformed", (long long)condition.id());
-                return false;
+        sp<ConditionTracker> tracker =
+                createConditionTracker(key, condition, i, atomMatchingTrackerMap);
+        if (tracker == nullptr) {
+            return false;
         }
+        allConditionTrackers.push_back(tracker);
         if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
             ALOGE("Duplicate Predicate found!");
             return false;
         }
-        conditionTrackerMap[condition.id()] = index;
+        conditionTrackerMap[condition.id()] = i;
         conditionConfigs.push_back(condition);
     }
 
@@ -348,12 +384,20 @@
 }
 
 bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
-                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+                map<int64_t, uint64_t>& stateProtoHashes) {
     for (int i = 0; i < config.state_size(); i++) {
         const State& state = config.state(i);
         const int64_t stateId = state.id();
         stateAtomIdMap[stateId] = state.atom_id();
 
+        string serializedState;
+        if (!state.SerializeToString(&serializedState)) {
+            ALOGE("Unable to serialize state %lld", (long long)stateId);
+            return false;
+        }
+        stateProtoHashes[stateId] = Hash64(serializedState);
+
         const StateMap& stateMap = state.map();
         for (auto group : stateMap.group()) {
             for (auto value : group.value()) {
@@ -448,18 +492,25 @@
             }
         }
 
+        bool hasActivation = false;
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        uint64_t activationHash;
         bool success = handleMetricActivation(
                 config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
-                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
+                hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
         if (!success) return false;
 
-        sp<MetricProducer> countProducer =
-                new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
-                                        timeBaseTimeNs, currentTimeNs, eventActivationMap,
-                                        eventDeactivationMap, slicedStateAtoms, stateGroupMap);
+        uint64_t metricHash;
+        if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+            return false;
+        }
+
+        sp<MetricProducer> countProducer = new CountMetricProducer(
+                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
+                timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
+                slicedStateAtoms, stateGroupMap);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -557,19 +608,26 @@
             }
         }
 
+        bool hasActivation = false;
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        uint64_t activationHash;
         bool success = handleMetricActivation(
                 config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
-                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
+                hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
         if (!success) return false;
 
+        uint64_t metricHash;
+        if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+            return false;
+        }
+
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
-                trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions,
-                timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
-                slicedStateAtoms, stateGroupMap);
+                trackerIndices[1], trackerIndices[2], nesting, wizard, metricHash,
+                internalDimensions, timeBaseTimeNs, currentTimeNs, eventActivationMap,
+                eventDeactivationMap, slicedStateAtoms, stateGroupMap);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -605,17 +663,24 @@
             }
         }
 
+        bool hasActivation = false;
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        uint64_t activationHash;
         bool success = handleMetricActivation(
                 config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
-                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
+                hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
         if (!success) return false;
 
-        sp<MetricProducer> eventMetric =
-                new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
-                                        timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
+        uint64_t metricHash;
+        if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+            return false;
+        }
+
+        sp<MetricProducer> eventMetric = new EventMetricProducer(
+                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
+                timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
 
         allMetricProducers.push_back(eventMetric);
     }
@@ -694,18 +759,26 @@
             }
         }
 
+        bool hasActivation = false;
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        uint64_t activationHash;
         bool success = handleMetricActivation(
                 config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
-                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
+                hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
         if (!success) return false;
 
+        uint64_t metricHash;
+        if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+            return false;
+        }
+
         sp<MetricProducer> valueProducer = new ValueMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
-                matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager,
-                eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
+                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
+                trackerIndex, matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs,
+                pullerManager, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+                stateGroupMap);
         allMetricProducers.push_back(valueProducer);
     }
 
@@ -790,18 +863,25 @@
             }
         }
 
+        bool hasActivation = false;
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+        uint64_t activationHash;
         bool success = handleMetricActivation(
                 config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
-                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
+                hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
         if (!success) return false;
 
+        uint64_t metricHash;
+        if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+            return false;
+        }
+
         sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
-                matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs,
-                pullerManager, eventActivationMap, eventDeactivationMap);
+                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
+                trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs,
+                currentTimeNs, pullerManager, eventActivationMap, eventDeactivationMap);
         allMetricProducers.push_back(gaugeProducer);
     }
     for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -934,7 +1014,9 @@
                       vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
                       unordered_map<int64_t, int>& atomMatchingTrackerMap,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
+                      unordered_map<int64_t, int>& conditionTrackerMap,
                       vector<sp<MetricProducer>>& allMetricProducers,
+                      unordered_map<int64_t, int>& metricProducerMap,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
                       vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
                       unordered_map<int, std::vector<int>>& conditionToMetricMap,
@@ -943,10 +1025,9 @@
                       unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
                       unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
                       unordered_map<int64_t, int>& alertTrackerMap,
-                      vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) {
-    unordered_map<int64_t, int> conditionTrackerMap;
+                      vector<int>& metricsWithActivation, map<int64_t, uint64_t>& stateProtoHashes,
+                      set<int64_t>& noReportMetricIds) {
     vector<ConditionState> initialConditionCache;
-    unordered_map<int64_t, int> metricProducerMap;
     unordered_map<int64_t, int> stateAtomIdMap;
     unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
 
@@ -963,7 +1044,7 @@
         return false;
     }
 
-    if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
+    if (!initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)) {
         ALOGE("initStates failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index 4cfd1b0..4979c30 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -42,6 +42,17 @@
 sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
                                                   const sp<UidMap>& uidMap);
 
+// Create a ConditionTracker.
+// input:
+// [predicate]: the input Predicate from the StatsdConfig
+// [index]: the index of the condition tracker
+// [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers
+// output:
+// new ConditionTracker, or null if the tracker is unable to be created
+sp<ConditionTracker> createConditionTracker(
+        const ConfigKey& key, const Predicate& predicate, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap);
+
 // Helper functions for MetricsManager to initialize from StatsdConfig.
 // *Note*: only initStatsdConfig() should be called from outside.
 // All other functions are intermediate
@@ -77,7 +88,6 @@
                     std::unordered_map<int64_t, int>& conditionTrackerMap,
                     std::vector<sp<ConditionTracker>>& allConditionTrackers,
                     std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                    std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
                     std::vector<ConditionState>& initialConditionCache);
 
 // Initialize State maps using State protos in the config. These maps will
@@ -88,8 +98,10 @@
 // [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
 // [allStateGroupMaps]: this map should contain the mapping from states ids and state
 //                      values to state group ids for all states
+// [stateProtoHashes]: contains a map of state id to the hash of the State proto from the config
 bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
-                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+                std::map<int64_t, uint64_t>& stateProtoHashes);
 
 // Initialize MetricProducers.
 // input:
@@ -111,7 +123,6 @@
         const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
-        const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const unordered_map<int64_t, int>& stateAtomIdMap,
         const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
@@ -135,7 +146,9 @@
                       std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
                       std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                      std::unordered_map<int64_t, int>& conditionTrackerMap,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
+                      std::unordered_map<int64_t, int>& metricProducerMap,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
                       vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
                       std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
@@ -144,7 +157,9 @@
                       unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
                       unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
                       std::unordered_map<int64_t, int>& alertTrackerMap,
-                      vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds);
+                      vector<int>& metricsWithActivation,
+                      std::map<int64_t, uint64_t>& stateProtoHashes,
+                      std::set<int64_t>& noReportMetricIds);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 07b5311b..8998b5f 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -39,6 +39,7 @@
 const int ATTRIBUTION_NODE_FIELD_ID = 1;
 const int ATTRIBUTION_UID_FIELD_ID = 1;
 const int TAG_ID = 1;
+const uint64_t protoHash = 0x123456789;
 
 SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
                                          bool outputSlicedUid, Position position) {
@@ -123,7 +124,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -177,7 +178,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -231,8 +232,9 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/,
-                                            simplePredicate, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
+                                            0 /*tracker index*/, simplePredicate,
+                                            trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
 
     // This event is not accessed in this test besides dimensions which is why this is okay.
@@ -317,7 +319,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
@@ -392,7 +394,7 @@
         trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
         trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
 
@@ -514,7 +516,7 @@
     trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
     trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -610,7 +612,7 @@
         trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
         trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
 
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index bb8e7bf..8e2864c 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -41,6 +41,7 @@
 
 namespace {
 const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
 
 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -75,7 +76,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+                                      wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
     EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, countProducer.mCurrentBucketNum);
     EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -95,7 +96,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, bucketStartTimeNs, bucketStartTimeNs);
+                                      wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     // 2 events in bucket 1.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -158,7 +159,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
-                                      bucketStartTimeNs, bucketStartTimeNs);
+                                      protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
 
@@ -226,8 +227,8 @@
     EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
 
     CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
-                                      {ConditionState::kUnknown}, wizard, bucketStartTimeNs,
-                                      bucketStartTimeNs);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      bucketStartTimeNs, bucketStartTimeNs);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -265,7 +266,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
-                                      bucketStartTimeNs, bucketStartTimeNs);
+                                      protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -332,7 +333,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
-                                      bucketStartTimeNs, bucketStartTimeNs);
+                                      protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     // Bucket is flushed yet.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -397,7 +398,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, bucketStartTimeNs, bucketStartTimeNs);
+                                      wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
 
@@ -459,7 +460,7 @@
     int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
-                                      oneDayNs, fiveWeeksNs);
+                                      protoHash, oneDayNs, fiveWeeksNs);
 
     int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
 
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 05cfa37b..d1f8977 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -45,6 +45,7 @@
 namespace {
 
 const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
     AStatsEvent_setAtomId(statsEvent, atomId);
@@ -71,10 +72,10 @@
 
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5,
+            600 * NS_PER_SEC + NS_PER_SEC / 2);
 
     EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
@@ -99,10 +100,10 @@
 
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
 
     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -145,7 +146,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
-            wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
     durationProducer.mCondition = ConditionState::kFalse;
 
     EXPECT_FALSE(durationProducer.mCondition);
@@ -195,7 +196,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
-            wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -238,10 +239,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -301,10 +302,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -365,10 +366,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -411,10 +412,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -465,10 +466,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index dfbb9da..4bbbd2c 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -36,9 +36,11 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, 12345);
 
 namespace {
+const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
+
 void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
     AStatsEvent_setAtomId(statsEvent, atomId);
@@ -66,7 +68,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, bucketStartTimeNs);
+                                      wizard, protoHash, bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -102,7 +104,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, bucketStartTimeNs);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      bucketStartTimeNs);
 
     eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -157,7 +160,8 @@
     EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
 
     EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, bucketStartTimeNs);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index ba919f1..1060681 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -47,6 +47,7 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
+const uint64_t protoHash = 0x123456789;
 const int logEventMatcherIndex = 0;
 const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -101,8 +102,9 @@
     // statsd started long ago.
     // The metric starts in the middle of the bucket
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, -1, -1,
-                                      tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
@@ -139,8 +141,9 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -213,7 +216,7 @@
             createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
                                       -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
     gaugeProducer.prepareFirstBucket();
@@ -306,8 +309,9 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -373,8 +377,9 @@
             .WillOnce(Return(false));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -426,9 +431,9 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onConditionChanged(true, conditionChangeNs);
@@ -509,9 +514,9 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
@@ -554,8 +559,9 @@
             createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     Alert alert;
@@ -649,8 +655,8 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     gaugeProducer.prepareFirstBucket();
 
@@ -724,8 +730,8 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     gaugeProducer.prepareFirstBucket();
 
@@ -783,8 +789,8 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     gaugeProducer.prepareFirstBucket();
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1000aea..b166cc1 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -46,6 +46,7 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
+const uint64_t protoHash = 0x1234567890;
 const int logEventMatcherIndex = 0;
 const int64_t bucketStartTimeNs = 10000000000;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -57,7 +58,7 @@
 double epsilon = 0.001;
 
 static void assertPastBucketValuesSingleKey(
-        const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
+        const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets,
         const std::initializer_list<int>& expectedValuesList,
         const std::initializer_list<int64_t>& expectedDurationNsList,
         const std::initializer_list<int64_t>& expectedStartTimeNsList,
@@ -79,7 +80,7 @@
     ASSERT_EQ(1, mPastBuckets.size());
     ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
 
-    const vector<ValueBucket>& buckets = mPastBuckets.begin()->second;
+    const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second;
     for (int i = 0; i < expectedValues.size(); i++) {
         EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
                 << "Values differ at index " << i;
@@ -108,8 +109,8 @@
 
         sp<ValueMetricProducer> valueProducer =
                 new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                        wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                        bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                        wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                        tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
         valueProducer->prepareFirstBucket();
         return valueProducer;
     }
@@ -127,7 +128,7 @@
 
         sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
                 kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
-                logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+                protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
                 bucketStartTimeNs, pullerManager);
         valueProducer->prepareFirstBucket();
         valueProducer->mCondition = conditionAfterFirstBucketPrepared;
@@ -147,9 +148,9 @@
                 .WillRepeatedly(Return());
 
         sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-                kConfigKey, metric, -1 /* no condition */, {}, wizard, logEventMatcherIndex,
-                eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
-                {}, slicedStateAtoms, stateGroupMap);
+                kConfigKey, metric, -1 /* no condition */, {}, wizard, protoHash,
+                logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+                bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
         valueProducer->prepareFirstBucket();
         return valueProducer;
     }
@@ -169,8 +170,9 @@
 
         sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
                 kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown},
-                wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
-                bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
+                wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
+                bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms,
+                stateGroupMap);
         valueProducer->prepareFirstBucket();
         valueProducer->mCondition = conditionAfterFirstBucketPrepared;
         return valueProducer;
@@ -228,8 +230,8 @@
     // statsd started long ago.
     // The metric starts in the middle of the bucket
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, -1,
-                                      startTimeBase, 22, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      -1, startTimeBase, 22, pullerManager);
     valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
@@ -254,8 +256,8 @@
     // statsd started long ago.
     // The metric starts in the middle of the bucket
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, -1, 5,
-                                      600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
     valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
@@ -288,8 +290,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -304,8 +307,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -322,8 +325,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -413,9 +416,10 @@
                 return true;
             }));
 
-    sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-            kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, logEventMatcherIndex,
-            eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard,
+                                    protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
+                                    bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer->prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -426,8 +430,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -455,8 +460,8 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     // the base was reset
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -489,8 +494,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -502,8 +508,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -516,8 +522,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -549,8 +555,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -562,8 +569,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -573,8 +580,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -624,8 +631,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -641,8 +649,8 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(110, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -654,8 +662,8 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -674,9 +682,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -744,9 +752,9 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -792,9 +800,9 @@
     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Return(true));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -864,9 +872,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -879,8 +887,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -888,7 +897,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(30, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -905,7 +914,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      protoHash, logEventMatcherIndex, eventMatcherWizard, -1,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
@@ -925,8 +934,8 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     LogEvent event3(/*uid=*/0, /*pid=*/0);
@@ -935,7 +944,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
@@ -946,7 +955,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -972,7 +981,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
                                       -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     valueProducer.prepareFirstBucket();
@@ -1089,8 +1098,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     // startUpdated:true sum:0 start:11
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1104,8 +1114,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // tartUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -1121,8 +1131,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -1180,8 +1190,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1189,8 +1200,8 @@
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -1203,8 +1214,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -1252,8 +1263,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1265,8 +1277,8 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 
@@ -1274,8 +1286,8 @@
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(130, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1286,8 +1298,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
 
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1312,9 +1324,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1327,7 +1339,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1335,7 +1347,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1352,9 +1364,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1364,7 +1376,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1374,7 +1386,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1391,9 +1403,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1405,7 +1417,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval;
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(1, curInterval.sampleSize);
@@ -1414,7 +1426,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
     EXPECT_EQ(2, curInterval.sampleSize);
 
@@ -1435,9 +1447,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1449,7 +1461,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1457,7 +1469,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1475,9 +1487,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1487,8 +1499,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1499,7 +1512,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
 
@@ -1509,8 +1522,8 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
 
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1520,8 +1533,8 @@
     CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1543,9 +1556,9 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1558,12 +1571,13 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(20, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1572,12 +1586,12 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
 
@@ -1587,14 +1601,14 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(25, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1604,13 +1618,13 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(29, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1656,9 +1670,9 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
-    auto& baseInfo1 = iterBase->second[0];
+    auto& baseInfo1 = iterBase->second.baseInfos[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1692,8 +1706,8 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto& interval2 = it->second[0];
-    auto& baseInfo2 = itBase->second[0];
+    auto& interval2 = it->second.intervals[0];
+    auto& baseInfo2 = itBase->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1732,9 +1746,10 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     const auto& it = valueProducer->mCurrentSlicedBucket.begin();
-    ValueMetricProducer::Interval& interval1 = it->second[0];
+    ValueMetricProducer::Interval& interval1 = it->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo1 =
-            valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1761,9 +1776,10 @@
         }
     }
     EXPECT_TRUE(it2 != it);
-    ValueMetricProducer::Interval& interval2 = it2->second[0];
+    ValueMetricProducer::Interval& interval2 = it2->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo2 =
-            valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1792,14 +1808,16 @@
     // Get new references now that entries have been deleted from the map
     const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
     const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
-    ASSERT_EQ(it3->second.size(), 1);
-    ASSERT_EQ(it4->second.size(), 1);
-    ValueMetricProducer::Interval& interval3 = it3->second[0];
-    ValueMetricProducer::Interval& interval4 = it4->second[0];
+    ASSERT_EQ(it3->second.intervals.size(), 1);
+    ASSERT_EQ(it4->second.intervals.size(), 1);
+    ValueMetricProducer::Interval& interval3 = it3->second.intervals[0];
+    ValueMetricProducer::Interval& interval4 = it4->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo3 =
-            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     ValueMetricProducer::BaseInfo& baseInfo4 =
-            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
 
     EXPECT_EQ(true, baseInfo3.hasBase);
     EXPECT_EQ(5, baseInfo3.base.long_value);
@@ -1837,9 +1855,9 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
-    auto& baseInfo1 = iterBase->second[0];
+    auto& baseInfo1 = iterBase->second.baseInfos[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1875,8 +1893,8 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto interval2 = it->second[0];
-    auto baseInfo2 = itBase->second[0];
+    auto interval2 = it->second.intervals[0];
+    auto baseInfo2 = itBase->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1889,8 +1907,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     // Only one interval left. One was trimmed.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(5, baseInfo2.base.long_value);
@@ -1903,8 +1921,8 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(14, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
@@ -1943,8 +1961,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo& curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1980,8 +1999,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo& curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2030,8 +2050,9 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2071,7 +2092,7 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
                                       bucket2StartTimeNs, bucket2StartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
@@ -2103,8 +2124,9 @@
     valueProducer->mHasGlobalBase = true;
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2156,8 +2178,9 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2294,8 +2317,9 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2373,8 +2397,9 @@
     // Last pull failed so base has been reset.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2460,8 +2485,9 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2469,8 +2495,8 @@
     // Empty pull.
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2513,8 +2539,9 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2524,8 +2551,8 @@
     allData.clear();
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // Data is empty, base should be reset.
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(5, curBaseInfo.base.long_value);
@@ -2570,14 +2597,14 @@
     ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     auto iterator = valueProducer->mCurrentSlicedBucket.begin();
     auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
-    EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
-    EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(true, baseInfoIter->second.baseInfos[0].hasBase);
+    EXPECT_EQ(2, baseInfoIter->second.baseInfos[0].base.long_value);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
     iterator++;
     baseInfoIter++;
-    EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
-    EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(false, baseInfoIter->second.baseInfos[0].hasBase);
+    EXPECT_EQ(1, baseInfoIter->second.baseInfos[0].base.long_value);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
 
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
@@ -2676,8 +2703,8 @@
 
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(5, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2811,8 +2838,8 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
 
@@ -2898,9 +2925,9 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
@@ -2932,9 +2959,9 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -2981,9 +3008,9 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
@@ -3045,8 +3072,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
@@ -3058,8 +3086,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -3091,8 +3119,9 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -3984,18 +4013,18 @@
     // Base for dimension key {}
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
@@ -4005,19 +4034,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change ON->OFF.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
@@ -4027,26 +4056,26 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(9, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, ON}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change OFF->ON.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
@@ -4056,35 +4085,35 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(21, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, OFF}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(12, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(12, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, ON}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
@@ -4195,18 +4224,18 @@
     // Base for dimension key {}
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, {kStateUnknown}}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
@@ -4216,19 +4245,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.long_value);
+              itBase->second.currentState.getValues()[0].mValue.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change ON->VR.
     // Both ON and VR are in the same state group, so the base should not change.
@@ -4239,19 +4268,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change VR->ON.
     // Both ON and VR are in the same state group, so the base should not change.
@@ -4262,19 +4291,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change VR->OFF.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
@@ -4284,27 +4313,27 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(21, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOffGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, ON GROUP}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
               it->first.getStateValuesKey().getValues()[0].mValue.long_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(16, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(16, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
@@ -4447,35 +4476,35 @@
     // Base for dimension key {uid 1}.
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{uid 1}, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(7, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{uid 2}, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
     auto uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4485,36 +4514,36 @@
     // Base for dimension key {uid 1}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(6, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(7, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 2 process state change kStateUnknown -> Background.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4524,36 +4553,36 @@
     // Base for dimension key {uid 1}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(6, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(9, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4570,36 +4599,36 @@
     it = valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(10, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(10, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4608,7 +4637,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4617,7 +4646,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change from Foreground -> Background.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4630,35 +4659,35 @@
     // Base for dimension key {uid 2}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(13, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(13, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Value for key {uid 1, FOREGROUND}
     it++;
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4666,8 +4695,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
     // Value for key {uid 2, kStateUnknown}
     it++;
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4675,7 +4704,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change Background->Foreground.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4687,36 +4716,36 @@
     // Base for dimension key {uid 2}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(17, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(17, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 1, BACKGROUND}
     it++;
@@ -4725,8 +4754,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4735,8 +4764,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4857,23 +4886,23 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
-    std::unordered_map<HashableDimensionKey, std::vector<ValueMetricProducer::BaseInfo>>::iterator
+    std::unordered_map<HashableDimensionKey, ValueMetricProducer::DimensionsInWhatInfo>::iterator
             itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, -1}
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    std::unordered_map<MetricDimensionKey, std::vector<ValueMetricProducer::Interval>>::iterator
-            it = valueProducer->mCurrentSlicedBucket.begin();
+    std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
+            valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after battery saver mode OFF event.
     unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4882,12 +4911,12 @@
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, ON}
     ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     it = valueProducer->mCurrentSlicedBucket.begin();
@@ -4895,8 +4924,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4909,23 +4938,23 @@
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(11, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(11, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
 
     // Bucket 2 status after condition change to false.
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_FALSE(itBase->second[0].hasBase);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_FALSE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, OFF}
     ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
     it = valueProducer->mCurrentSlicedBucket.begin();
@@ -4933,8 +4962,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 8c698eb..843d836 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -14,6 +14,7 @@
 
 #include "src/metrics/parsing_utils/config_update_utils.h"
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 #include <stdio.h>
@@ -23,6 +24,8 @@
 #include <vector>
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "src/condition/CombinationConditionTracker.h"
+#include "src/condition/SimpleConditionTracker.h"
 #include "src/matchers/CombinationAtomMatchingTracker.h"
 #include "src/metrics/parsing_utils/metrics_manager_util.h"
 #include "tests/statsd_test_util.h"
@@ -53,7 +56,9 @@
 vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers;
 unordered_map<int64_t, int> oldAtomMatchingTrackerMap;
 vector<sp<ConditionTracker>> oldConditionTrackers;
+unordered_map<int64_t, int> oldConditionTrackerMap;
 vector<sp<MetricProducer>> oldMetricProducers;
+unordered_map<int64_t, int> oldMetricProducerMap;
 std::vector<sp<AnomalyTracker>> oldAnomalyTrackers;
 std::vector<sp<AlarmTracker>> oldAlarmTrackers;
 unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -63,6 +68,7 @@
 unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
 unordered_map<int64_t, int> alertTrackerMap;
 vector<int> metricsWithActivation;
+map<int64_t, uint64_t> oldStateHashes;
 std::set<int64_t> noReportMetricIds;
 
 class ConfigUpdateTest : public ::testing::Test {
@@ -75,7 +81,9 @@
         oldAtomMatchingTrackers.clear();
         oldAtomMatchingTrackerMap.clear();
         oldConditionTrackers.clear();
+        oldConditionTrackerMap.clear();
         oldMetricProducers.clear();
+        oldMetricProducerMap.clear();
         oldAnomalyTrackers.clear();
         oldAlarmTrackers.clear();
         conditionToMetricMap.clear();
@@ -85,6 +93,7 @@
         deactivationAtomTrackerToMetricMap.clear();
         alertTrackerMap.clear();
         metricsWithActivation.clear();
+        oldStateHashes.clear();
         noReportMetricIds.clear();
     }
 };
@@ -93,10 +102,11 @@
     return initStatsdConfig(
             key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
             timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap,
-            oldConditionTrackers, oldMetricProducers, oldAnomalyTrackers, oldAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds);
+            oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap,
+            oldAnomalyTrackers, oldAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            oldStateHashes, noReportMetricIds);
 }
 
 }  // anonymous namespace
@@ -144,6 +154,30 @@
     EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
 }
 
+TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) {
+    StatsdConfig config;
+    AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+    *config.add_atom_matcher() = matcher;
+
+    EXPECT_TRUE(initConfig(config));
+
+    StatsdConfig newConfig;
+    // Different id, so should be a new matcher.
+    AtomMatcher newMatcher = CreateSimpleAtomMatcher("DIFFERENT_NAME", /*atom=*/10);
+    int64_t matcherId = newMatcher.id();
+    EXPECT_NE(matcherId, matcher.id());
+    *newConfig.add_atom_matcher() = newMatcher;
+
+    vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    newAtomMatchingTrackerMap[matcherId] = 0;
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW);
+}
+
 TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) {
     StatsdConfig config;
     AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
@@ -338,9 +372,10 @@
     set<int> newTagIds;
     unordered_map<int64_t, int> newAtomMatchingTrackerMap;
     vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
-    EXPECT_TRUE(updateAtomTrackers(newConfig, uidMap, oldAtomMatchingTrackerMap,
-                                   oldAtomMatchingTrackers, newTagIds, newAtomMatchingTrackerMap,
-                                   newAtomMatchingTrackers));
+    set<int64_t> replacedMatchers;
+    EXPECT_TRUE(updateAtomMatchingTrackers(
+            newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds,
+            newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers));
 
     ASSERT_EQ(newTagIds.size(), 3);
     EXPECT_EQ(newTagIds.count(10), 1);
@@ -405,8 +440,454 @@
     EXPECT_EQ(childMatchers->size(), 2);
     EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
     EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end());
+
+    // Expect replacedMatchers to have simple2 and combination2
+    ASSERT_EQ(replacedMatchers.size(), 2);
+    EXPECT_NE(replacedMatchers.find(simple2Id), replacedMatchers.end());
+    EXPECT_NE(replacedMatchers.find(combination2Id), replacedMatchers.end());
 }
 
+TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Modify the predicate.
+    config.mutable_predicate(0)->mutable_simple_predicate()->set_count_nesting(true);
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    int64_t startMatcherId = startMatcher.id();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Start matcher was replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(startMatcherId);
+
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Same predicates, different order
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. It should recurse the two child predicates and preserve all 3.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE);
+    EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Changing the logical operation changes the predicate definition, so it should be replaced.
+    combination1.mutable_combination()->set_operation(LogicalOperation::OR);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. The simple conditions should not be evaluated.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN);
+    EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    simple2.mutable_simple_predicate()->set_count_nesting(false);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. Simple2 and combination1 must be evaluated.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateConditions) {
+    StatsdConfig config;
+
+    // Add atom matchers. These are mostly needed for initStatsdConfig
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    AtomMatcher matcher6 = CreateBatterySaverModeStopAtomMatcher();
+    int64_t matcher6Id = matcher6.id();
+    *config.add_atom_matcher() = matcher6;
+
+    // Add the predicates.
+    // Will be preserved.
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    int64_t simple1Id = simple1.id();
+    *config.add_predicate() = simple1;
+
+    // Will be preserved.
+    Predicate simple2 = CreateScheduledJobPredicate();
+    int64_t simple2Id = simple2.id();
+    *config.add_predicate() = simple2;
+
+    // Will be replaced.
+    Predicate simple3 = CreateBatterySaverModePredicate();
+    int64_t simple3Id = simple3.id();
+    *config.add_predicate() = simple3;
+
+    // Will be preserved
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    combination1.mutable_combination()->set_operation(LogicalOperation::AND);
+    combination1.mutable_combination()->add_predicate(simple1Id);
+    combination1.mutable_combination()->add_predicate(simple2Id);
+    int64_t combination1Id = combination1.id();
+    *config.add_predicate() = combination1;
+
+    // Will be replaced since simple3 will be replaced.
+    Predicate combination2;
+    combination2.set_id(StringToId("COMBINATION2"));
+    combination2.mutable_combination()->set_operation(LogicalOperation::OR);
+    combination2.mutable_combination()->add_predicate(simple1Id);
+    combination2.mutable_combination()->add_predicate(simple3Id);
+    int64_t combination2Id = combination2.id();
+    *config.add_predicate() = combination2;
+
+    // Will be removed.
+    Predicate combination3;
+    combination3.set_id(StringToId("COMBINATION3"));
+    combination3.mutable_combination()->set_operation(LogicalOperation::NOT);
+    combination3.mutable_combination()->add_predicate(simple2Id);
+    int64_t combination3Id = combination3.id();
+    *config.add_predicate() = combination3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Mark marcher 5 as replaced. Causes simple3, and therefore combination2 to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher6Id);
+
+    // Change the condition of simple1 to true.
+    ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id);
+    LogEvent event(/*uid=*/0, /*pid=*/0);  // Empty event is fine since there are no dimensions.
+    // Mark the stop matcher as matched, condition should be false.
+    vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched);
+    eventMatcherValues[1] = MatchingState::kMatched;
+    vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated);
+    vector<bool> conditionChangeCache(6, false);
+    oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers,
+                                               tmpConditionCache, conditionChangeCache);
+    EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse);
+    EXPECT_EQ(conditionChangeCache[0], true);
+
+    // New combination matcher. Should have an initial condition of true since it is NOT(simple1).
+    Predicate combination4;
+    combination4.set_id(StringToId("COMBINATION4"));
+    combination4.mutable_combination()->set_operation(LogicalOperation::NOT);
+    combination4.mutable_combination()->add_predicate(simple1Id);
+    int64_t combination4Id = combination4.id();
+    *config.add_predicate() = combination4;
+
+    // Map the matchers in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher6Index = 0;
+    newAtomMatchingTrackerMap[matcher6Id] = 0;
+    const int matcher5Index = 1;
+    newAtomMatchingTrackerMap[matcher5Id] = 1;
+    const int matcher4Index = 2;
+    newAtomMatchingTrackerMap[matcher4Id] = 2;
+    const int matcher3Index = 3;
+    newAtomMatchingTrackerMap[matcher3Id] = 3;
+    const int matcher2Index = 4;
+    newAtomMatchingTrackerMap[matcher2Id] = 4;
+    const int matcher1Index = 5;
+    newAtomMatchingTrackerMap[matcher1Id] = 5;
+
+    StatsdConfig newConfig;
+    *newConfig.add_predicate() = simple3;
+    const int simple3Index = 0;
+    *newConfig.add_predicate() = combination2;
+    const int combination2Index = 1;
+    *newConfig.add_predicate() = combination4;
+    const int combination4Index = 2;
+    *newConfig.add_predicate() = simple2;
+    const int simple2Index = 3;
+    *newConfig.add_predicate() = combination1;
+    const int combination1Index = 4;
+    *newConfig.add_predicate() = simple1;
+    const int simple1Index = 5;
+
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    vector<sp<ConditionTracker>> newConditionTrackers;
+    unordered_map<int, vector<int>> trackerToConditionMap;
+    std::vector<ConditionState> conditionCache;
+    std::set<int64_t> replacedConditions;
+    EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers,
+                                 oldConditionTrackerMap, oldConditionTrackers,
+                                 newConditionTrackerMap, newConditionTrackers,
+                                 trackerToConditionMap, conditionCache, replacedConditions));
+
+    unordered_map<int64_t, int> expectedConditionTrackerMap = {
+            {simple1Id, simple1Index},           {simple2Id, simple2Index},
+            {simple3Id, simple3Index},           {combination1Id, combination1Index},
+            {combination2Id, combination2Index}, {combination4Id, combination4Index},
+    };
+    EXPECT_THAT(newConditionTrackerMap, ContainerEq(expectedConditionTrackerMap));
+
+    ASSERT_EQ(newConditionTrackers.size(), 6);
+    // Make sure all conditions are initialized:
+    for (const sp<ConditionTracker>& tracker : newConditionTrackers) {
+        EXPECT_TRUE(tracker->mInitialized);
+    }
+
+    // Make sure preserved conditions are the same.
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple1Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple1Id)]);
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple2Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple2Id)]);
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(combination1Id)],
+              newConditionTrackers[newConditionTrackerMap.at(combination1Id)]);
+
+    // Make sure replaced conditions are different and included in replacedConditions.
+    EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(simple3Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple3Id)]);
+    EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(combination2Id)],
+              newConditionTrackers[newConditionTrackerMap.at(combination2Id)]);
+    EXPECT_THAT(replacedConditions, ContainerEq(set({simple3Id, combination2Id})));
+
+    // Verify the trackerToConditionMap
+    ASSERT_EQ(trackerToConditionMap.size(), 6);
+    const vector<int>& matcher1Conditions = trackerToConditionMap[matcher1Index];
+    EXPECT_THAT(matcher1Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+                                                         combination2Index, combination4Index));
+    const vector<int>& matcher2Conditions = trackerToConditionMap[matcher2Index];
+    EXPECT_THAT(matcher2Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+                                                         combination2Index, combination4Index));
+    const vector<int>& matcher3Conditions = trackerToConditionMap[matcher3Index];
+    EXPECT_THAT(matcher3Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+    const vector<int>& matcher4Conditions = trackerToConditionMap[matcher4Index];
+    EXPECT_THAT(matcher4Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+    const vector<int>& matcher5Conditions = trackerToConditionMap[matcher5Index];
+    EXPECT_THAT(matcher5Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+    const vector<int>& matcher6Conditions = trackerToConditionMap[matcher6Index];
+    EXPECT_THAT(matcher6Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+
+    // Verify the conditionCache. Specifically, simple1 is false and combination4 is true.
+    ASSERT_EQ(conditionCache.size(), 6);
+    EXPECT_EQ(conditionCache[simple1Index], ConditionState::kFalse);
+    EXPECT_EQ(conditionCache[simple2Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[simple3Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination1Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination2Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination4Index], ConditionState::kTrue);
+
+    // Verify tracker indices/ids are correct.
+    EXPECT_EQ(newConditionTrackers[simple1Index]->getConditionId(), simple1Id);
+    EXPECT_EQ(newConditionTrackers[simple1Index]->mIndex, simple1Index);
+    EXPECT_TRUE(newConditionTrackers[simple1Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[simple2Index]->getConditionId(), simple2Id);
+    EXPECT_EQ(newConditionTrackers[simple2Index]->mIndex, simple2Index);
+    EXPECT_TRUE(newConditionTrackers[simple2Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[simple3Index]->getConditionId(), simple3Id);
+    EXPECT_EQ(newConditionTrackers[simple3Index]->mIndex, simple3Index);
+    EXPECT_TRUE(newConditionTrackers[simple3Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination1Index]->getConditionId(), combination1Id);
+    EXPECT_EQ(newConditionTrackers[combination1Index]->mIndex, combination1Index);
+    EXPECT_FALSE(newConditionTrackers[combination1Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination2Index]->getConditionId(), combination2Id);
+    EXPECT_EQ(newConditionTrackers[combination2Index]->mIndex, combination2Index);
+    EXPECT_FALSE(newConditionTrackers[combination2Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination4Index]->getConditionId(), combination4Id);
+    EXPECT_EQ(newConditionTrackers[combination4Index]->mIndex, combination4Index);
+    EXPECT_FALSE(newConditionTrackers[combination4Index]->IsSimpleCondition());
+
+    // Verify preserved trackers have indices updated.
+    SimpleConditionTracker* simpleTracker1 =
+            static_cast<SimpleConditionTracker*>(newConditionTrackers[simple1Index].get());
+    EXPECT_EQ(simpleTracker1->mStartLogMatcherIndex, matcher1Index);
+    EXPECT_EQ(simpleTracker1->mStopLogMatcherIndex, matcher2Index);
+    EXPECT_EQ(simpleTracker1->mStopAllLogMatcherIndex, -1);
+
+    SimpleConditionTracker* simpleTracker2 =
+            static_cast<SimpleConditionTracker*>(newConditionTrackers[simple2Index].get());
+    EXPECT_EQ(simpleTracker2->mStartLogMatcherIndex, matcher3Index);
+    EXPECT_EQ(simpleTracker2->mStopLogMatcherIndex, matcher4Index);
+    EXPECT_EQ(simpleTracker2->mStopAllLogMatcherIndex, -1);
+
+    CombinationConditionTracker* combinationTracker1 = static_cast<CombinationConditionTracker*>(
+            newConditionTrackers[combination1Index].get());
+    EXPECT_THAT(combinationTracker1->mChildren, UnorderedElementsAre(simple1Index, simple2Index));
+    EXPECT_THAT(combinationTracker1->mUnSlicedChildren,
+                UnorderedElementsAre(simple1Index, simple2Index));
+    EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty());
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
index d6db4c1..0d0a896 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
@@ -14,6 +14,7 @@
 
 #include "src/metrics/parsing_utils/metrics_manager_util.h"
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 #include <stdio.h>
@@ -384,9 +385,11 @@
     StatsdConfig config = buildConfigWithDifferentPredicates();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -396,15 +399,17 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_TRUE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
     ASSERT_EQ(4u, allMetricProducers.size());
     ASSERT_EQ(5u, allConditionTrackers.size());
 
@@ -433,9 +438,11 @@
     StatsdConfig config = buildGoodConfig();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -445,16 +452,19 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_TRUE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
     ASSERT_EQ(1u, allMetricProducers.size());
+    EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0)));
     ASSERT_EQ(1u, allAnomalyTrackers.size());
     ASSERT_EQ(1u, noReportMetricIds.size());
     ASSERT_EQ(1u, alertTrackerMap.size());
@@ -470,9 +480,11 @@
     StatsdConfig config = buildDimensionMetricsWithMultiTags();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -482,15 +494,17 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -501,9 +515,11 @@
     StatsdConfig config = buildCircleMatchers();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -513,15 +529,17 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -532,9 +550,11 @@
     StatsdConfig config = buildMissingMatchers();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -544,14 +564,16 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -562,9 +584,11 @@
     StatsdConfig config = buildMissingPredicate();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -574,14 +598,16 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -592,9 +618,11 @@
     StatsdConfig config = buildCirclePredicates();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -604,15 +632,17 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -623,9 +653,11 @@
     StatsdConfig config = buildAlertWithUnknownMetric();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
     unordered_map<int, std::vector<int>> conditionToMetricMap;
@@ -635,20 +667,23 @@
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
     unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
-            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
-            metricsWithActivation, noReportMetricIds));
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) {
     sp<UidMap> uidMap = new UidMap();
     AtomMatcher matcher;
+    // Matcher has no contents_case (simple/combination), so it is invalid.
     matcher.set_id(21);
     EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr);
 }
@@ -699,6 +734,65 @@
     ASSERT_EQ(atomIds.size(), 0);
 }
 
+TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) {
+    const ConfigKey key(123, 456);
+    // Predicate has no contents_case (simple/combination), so it is invalid.
+    Predicate predicate;
+    predicate.set_id(21);
+    unordered_map<int64_t, int> atomTrackerMap;
+    EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) {
+    int index = 1;
+    int64_t id = 987;
+    const ConfigKey key(123, 456);
+
+    int startMatcherIndex = 2, stopMatcherIndex = 0, stopAllMatcherIndex = 1;
+    int64_t startMatcherId = 246, stopMatcherId = 153, stopAllMatcherId = 975;
+
+    Predicate predicate;
+    predicate.set_id(id);
+    SimplePredicate* simplePredicate = predicate.mutable_simple_predicate();
+    simplePredicate->set_start(startMatcherId);
+    simplePredicate->set_stop(stopMatcherId);
+    simplePredicate->set_stop_all(stopAllMatcherId);
+
+    unordered_map<int64_t, int> atomTrackerMap;
+    atomTrackerMap[startMatcherId] = startMatcherIndex;
+    atomTrackerMap[stopMatcherId] = stopMatcherIndex;
+    atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex;
+
+    sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+    EXPECT_EQ(tracker->getConditionId(), id);
+    EXPECT_EQ(tracker->isSliced(), false);
+    EXPECT_TRUE(tracker->IsSimpleCondition());
+    const set<int>& interestedMatchers = tracker->getAtomMatchingTrackerIndex();
+    ASSERT_EQ(interestedMatchers.size(), 3);
+    ASSERT_EQ(interestedMatchers.count(startMatcherIndex), 1);
+    ASSERT_EQ(interestedMatchers.count(stopMatcherIndex), 1);
+    ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) {
+    int index = 1;
+    int64_t id = 987;
+    const ConfigKey key(123, 456);
+
+    Predicate predicate;
+    predicate.set_id(id);
+    Predicate_Combination* combinationPredicate = predicate.mutable_combination();
+    combinationPredicate->set_operation(LogicalOperation::AND);
+    combinationPredicate->add_predicate(888);
+    combinationPredicate->add_predicate(777);
+
+    // Combination conditions must be initialized to set most state.
+    unordered_map<int64_t, int> atomTrackerMap;
+    sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+    EXPECT_EQ(tracker->getConditionId(), id);
+    EXPECT_FALSE(tracker->IsSimpleCondition());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index caca05a..763ce6c6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5418,13 +5418,12 @@
 
         final int prevState = r.getLifecycleState();
 
-        if (prevState < ON_RESUME || prevState > ON_STOP) {
-            Log.w(TAG, "Activity state must be in [ON_RESUME..ON_STOP] in order to be relaunched,"
+        if (prevState < ON_START || prevState > ON_STOP) {
+            Log.w(TAG, "Activity state must be in [ON_START..ON_STOP] in order to be relaunched,"
                     + "current state is " + prevState);
             return;
         }
 
-
         // Initialize a relaunch request.
         final MergedConfiguration mergedConfiguration = new MergedConfiguration(
                 r.createdConfig != null ? r.createdConfig : mConfiguration,
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0055711..7087b60 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1160,9 +1160,17 @@
     // TODO: Add as AppProtoEnums
     public static final int OP_PHONE_CALL_CAMERA = 101;
 
+    /**
+     * Audio is being recorded for hotword detection.
+     *
+     * @hide
+     */
+    // TODO: Add as AppProtoEnums
+    public static final int OP_RECORD_AUDIO_HOTWORD = 102;
+
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 102;
+    public static final int _NUM_OP = 103;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1497,6 +1505,13 @@
      */
     public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera";
 
+    /**
+     * Audio is being recorded for hotword detection.
+     *
+     * @hide
+     */
+    public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1688,6 +1703,7 @@
             OP_NO_ISOLATED_STORAGE,             // NO_ISOLATED_STORAGE
             OP_PHONE_CALL_MICROPHONE,           // OP_PHONE_CALL_MICROPHONE
             OP_PHONE_CALL_CAMERA,               // OP_PHONE_CALL_CAMERA
+            OP_RECORD_AUDIO_HOTWORD,            // RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -1796,6 +1812,7 @@
             OPSTR_NO_ISOLATED_STORAGE,
             OPSTR_PHONE_CALL_MICROPHONE,
             OPSTR_PHONE_CALL_CAMERA,
+            OPSTR_RECORD_AUDIO_HOTWORD,
     };
 
     /**
@@ -1905,6 +1922,7 @@
             "NO_ISOLATED_STORAGE",
             "PHONE_CALL_MICROPHONE",
             "PHONE_CALL_CAMERA",
+            "RECORD_AUDIO_HOTWORD",
     };
 
     /**
@@ -2015,6 +2033,7 @@
             null, // no permission for OP_NO_ISOLATED_STORAGE
             null, // no permission for OP_PHONE_CALL_MICROPHONE
             null, // no permission for OP_PHONE_CALL_CAMERA
+            null, // no permission for OP_RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2125,6 +2144,7 @@
             null, // NO_ISOLATED_STORAGE
             null, // PHONE_CALL_MICROPHONE
             null, // PHONE_CALL_MICROPHONE
+            null, // RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2234,6 +2254,7 @@
             null, // NO_ISOLATED_STORAGE
             null, // PHONE_CALL_MICROPHONE
             null, // PHONE_CALL_CAMERA
+            null, // RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2342,6 +2363,7 @@
             AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE
             AppOpsManager.MODE_ALLOWED, // PHONE_CALL_MICROPHONE
             AppOpsManager.MODE_ALLOWED, // PHONE_CALL_CAMERA
+            AppOpsManager.MODE_ALLOWED, // OP_RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2454,6 +2476,7 @@
             true, // NO_ISOLATED_STORAGE
             false, // PHONE_CALL_MICROPHONE
             false, // PHONE_CALL_CAMERA
+            false, // RECORD_AUDIO_HOTWORD
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 340d5a1..2780036 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -73,6 +73,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelableException;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -107,7 +108,11 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.IOException;
 import java.lang.ref.WeakReference;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -135,6 +140,12 @@
     // Default flags to use with PackageManager when no flags are given.
     private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES;
 
+    /** Default set of checksums - includes all available checksums.
+     * @see PackageManager#getChecksums  */
+    private static final int DEFAULT_CHECKSUMS =
+            WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512
+                    | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512;
+
     // Name of the resource which provides background permission button string
     public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
             "app_permission_button_allow_always";
@@ -945,6 +956,39 @@
         }
     }
 
+    private static List<byte[]> encodeCertificates(List<Certificate> certs) throws
+            CertificateEncodingException {
+        if (certs == null) {
+            return null;
+        }
+        List<byte[]> result = new ArrayList<>(certs.size());
+        for (Certificate cert : certs) {
+            if (!(cert instanceof X509Certificate)) {
+                throw new CertificateEncodingException("Only X509 certificates supported.");
+            }
+            result.add(cert.getEncoded());
+        }
+        return result;
+    }
+
+    @Override
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+            @NonNull IntentSender statusReceiver)
+            throws CertificateEncodingException, IOException, NameNotFoundException {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(statusReceiver);
+        try {
+            mPM.getChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
+                    encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(PackageManager.NameNotFoundException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Wrap the cached value in a class that does deep compares on string
      * arrays.  The comparison is needed only for the verification mode of
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cee607f..fef8d10 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1924,10 +1924,8 @@
     @Override
     public Object getSystemService(String name) {
         if (vmIncorrectContextUseEnabled()) {
-            // We may override this API from outer context.
-            final boolean isUiContext = isUiContext() || isOuterUiContext();
             // Check incorrect Context usage.
-            if (isUiComponent(name) && !isUiContext) {
+            if (isUiComponent(name) && !isSelfOrOuterUiContext()) {
                 final String errorMessage = "Tried to access visual service "
                         + SystemServiceRegistry.getSystemServiceClassName(name)
                         + " from a non-visual Context:" + getOuterContext();
@@ -1944,15 +1942,17 @@
         return SystemServiceRegistry.getSystemService(this, name);
     }
 
-    private boolean isOuterUiContext() {
-        return getOuterContext() != null && getOuterContext().isUiContext();
-    }
-
     @Override
     public String getSystemServiceName(Class<?> serviceClass) {
         return SystemServiceRegistry.getSystemServiceName(serviceClass);
     }
 
+    // TODO(b/149463653): check if we still need this method after migrating IMS to WindowContext.
+    private boolean isSelfOrOuterUiContext() {
+        // We may override outer context's isUiContext
+        return isUiContext() || getOuterContext() != null && getOuterContext().isUiContext();
+    }
+
     /** @hide */
     @Override
     public boolean isUiContext() {
@@ -2413,7 +2413,7 @@
         context.setResources(createResources(mToken, mPackageInfo, mSplitName, overrideDisplayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
                 mResources.getLoaders()));
-        context.mIsUiContext = isUiContext() || isOuterUiContext();
+        context.mIsUiContext = isSelfOrOuterUiContext();
         return context;
     }
 
@@ -2529,9 +2529,9 @@
 
     @Override
     public Display getDisplay() {
-        if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay) {
+        if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSelfOrOuterUiContext()) {
             throw new UnsupportedOperationException("Tried to obtain display from a Context not "
-                    + "associated with  one. Only visual Contexts (such as Activity or one created "
+                    + "associated with one. Only visual Contexts (such as Activity or one created "
                     + "with Context#createWindowContext) or ones created with "
                     + "Context#createDisplayContext are associated with displays. Other types of "
                     + "Contexts are typically related to background entities and may return an "
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 0a47248..f640119 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -99,7 +99,6 @@
     void unregisterUidObserver(in IUidObserver observer);
     boolean isUidActive(int uid, String callingPackage);
     int getUidProcessState(int uid, in String callingPackage);
-    boolean setSchedPolicyCgroup(int tid, int group);
     // =============== End of transactions used on native side as well ============================
 
     // Special low-level communication with activity manager.
@@ -695,4 +694,14 @@
      * android.permission.RESET_APP_ERRORS.
      */
     void resetAppErrors();
+
+    /**
+     * Control the app freezer state. Returns true in case of success, false if the operation
+     * didn't succeed (for example, when the app freezer isn't supported). 
+     * Handling the freezer state via this method is reentrant, that is it can be 
+     * disabled and re-enabled multiple times in parallel. As long as there's a 1:1 disable to
+     * enable match, the freezer is re-enabled at last enable only.
+     * @param enable set it to true to enable the app freezer, false to disable it.
+     */
+    boolean enableAppFreezer(in boolean enable);
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6737972..1ff48f6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -207,7 +207,7 @@
      * <p>
      * Avoids spamming the system with overly large strings such as full e-mails.
      */
-    private static final int MAX_CHARSEQUENCE_LENGTH = 5 * 1024;
+    private static final int MAX_CHARSEQUENCE_LENGTH = 1024;
 
     /**
      * Maximum entries of reply text that are accepted by Builder and friends.
@@ -7863,7 +7863,7 @@
              */
             public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender,
                     boolean remoteInputHistory) {
-                mText = text;
+                mText = safeCharSequence(text);
                 mTimestamp = timestamp;
                 mSender = sender;
                 mRemoteInputHistory = remoteInputHistory;
@@ -7977,7 +7977,7 @@
                 bundle.putLong(KEY_TIMESTAMP, mTimestamp);
                 if (mSender != null) {
                     // Legacy listeners need this
-                    bundle.putCharSequence(KEY_SENDER, mSender.getName());
+                    bundle.putCharSequence(KEY_SENDER, safeCharSequence(mSender.getName()));
                     bundle.putParcelable(KEY_SENDER_PERSON, mSender);
                 }
                 if (mDataMimeType != null) {
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index 67d94de..18c4d70 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.graphics.Rect;
@@ -46,6 +47,8 @@
         @Nullable
         private Rect mSourceRectHint;
 
+        private boolean mAutoEnterAllowed;
+
         /**
          * Sets the aspect ratio.  This aspect ratio is defined as the desired width / height, and
          * does not change upon device rotation.
@@ -103,6 +106,25 @@
         }
 
         /**
+         * Sets whether the system is allowed to automatically put the activity in
+         * picture-in-picture mode without needing/waiting for the activity to call
+         * {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}.
+         *
+         * If true, {@link Activity#onPictureInPictureRequested()} will never be called.
+         *
+         * This property is false by default.
+         * @param autoEnterAllowed {@code true} if the system is allowed to automatically put the
+         *                                  activity in picture-in-picture mode.
+         *
+         * @return this builder instance.
+         */
+        @NonNull
+        public Builder setAutoEnterAllowed(boolean autoEnterAllowed) {
+            mAutoEnterAllowed = autoEnterAllowed;
+            return this;
+        }
+
+        /**
          * @return an immutable {@link PictureInPictureParams} to be used when entering or updating
          * the activity in picture-in-picture.
          *
@@ -111,7 +133,7 @@
          */
         public PictureInPictureParams build() {
             PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions,
-                    mSourceRectHint);
+                    mSourceRectHint, mAutoEnterAllowed);
             return params;
         }
     }
@@ -136,6 +158,11 @@
     @Nullable
     private Rect mSourceRectHint;
 
+    /**
+     * Whether the system is allowed to automatically put the activity in picture-in-picture mode.
+     */
+    private boolean mAutoEnterAllowed;
+
     /** {@hide} */
     PictureInPictureParams() {
     }
@@ -152,14 +179,18 @@
         if (in.readInt() != 0) {
             mSourceRectHint = Rect.CREATOR.createFromParcel(in);
         }
+        if (in.readInt() != 0) {
+            mAutoEnterAllowed = in.readBoolean();
+        }
     }
 
     /** {@hide} */
     PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions,
-            Rect sourceRectHint) {
+            Rect sourceRectHint, boolean autoEnterAllowed) {
         mAspectRatio = aspectRatio;
         mUserActions = actions;
         mSourceRectHint = sourceRectHint;
+        mAutoEnterAllowed = autoEnterAllowed;
     }
 
     /**
@@ -176,6 +207,7 @@
         if (otherArgs.hasSourceBoundsHint()) {
             mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
         }
+        mAutoEnterAllowed = otherArgs.mAutoEnterAllowed;
     }
 
     /**
@@ -248,11 +280,20 @@
     }
 
     /**
+     * @return whether auto pip allowed.
+     * @hide
+     */
+    public boolean isAutoEnterAllowed() {
+        return mAutoEnterAllowed;
+    }
+
+    /**
      * @return True if no parameters are set
      * @hide
      */
     public boolean empty() {
-        return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio();
+        return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio()
+                && !mAutoEnterAllowed;
     }
 
     @Override
@@ -281,6 +322,8 @@
         } else {
             out.writeInt(0);
         }
+        out.writeInt(1);
+        out.writeBoolean(mAutoEnterAllowed);
     }
 
     public static final @android.annotation.NonNull Creator<PictureInPictureParams> CREATOR =
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 7cd3fca..9e4ab33 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -54,6 +54,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -245,7 +246,7 @@
     /**
      * A cache of DisplayId, DisplayAdjustments to Display.
      */
-    private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>>
+    private final ArrayMap<Pair<Integer, DisplayAdjustments>, SoftReference<Display>>
             mAdjustedDisplays = new ArrayMap<>();
 
     /**
@@ -373,25 +374,28 @@
                 ? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments();
         final Pair<Integer, DisplayAdjustments> key =
                 Pair.create(displayId, displayAdjustmentsCopy);
+        SoftReference<Display> sd;
         synchronized (this) {
-            WeakReference<Display> wd = mAdjustedDisplays.get(key);
-            if (wd != null) {
-                final Display display = wd.get();
-                if (display != null) {
-                    return display;
-                }
-            }
-            final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-            if (dm == null) {
-                // may be null early in system startup
-                return null;
-            }
-            final Display display = dm.getCompatibleDisplay(displayId, key.second);
-            if (display != null) {
-                mAdjustedDisplays.put(key, new WeakReference<>(display));
-            }
-            return display;
+            sd = mAdjustedDisplays.get(key);
         }
+        if (sd != null) {
+            final Display display = sd.get();
+            if (display != null) {
+                return display;
+            }
+        }
+        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+        if (dm == null) {
+            // may be null early in system startup
+            return null;
+        }
+        final Display display = dm.getCompatibleDisplay(displayId, key.second);
+        if (display != null) {
+            synchronized (this) {
+                mAdjustedDisplays.put(key, new SoftReference<>(display));
+            }
+        }
+        return display;
     }
 
     /**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 59997cc..3b11b0d 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -103,6 +103,7 @@
 import android.media.AudioManager;
 import android.media.MediaFrameworkInitializer;
 import android.media.MediaRouter;
+import android.media.MediaTranscodeManager;
 import android.media.midi.IMidiManager;
 import android.media.midi.MidiManager;
 import android.media.projection.MediaProjectionManager;
@@ -305,6 +306,15 @@
                 return new AudioManager(ctx);
             }});
 
+        registerService(Context.MEDIA_TRANSCODING_SERVICE, MediaTranscodeManager.class,
+                new CachedServiceFetcher<MediaTranscodeManager>() {
+                    @Override
+                    public MediaTranscodeManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return new MediaTranscodeManager(ctx);
+                    }
+                });
+
         registerService(Context.MEDIA_ROUTER_SERVICE, MediaRouter.class,
                 new CachedServiceFetcher<MediaRouter>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c61426d..7b3d9f1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9751,21 +9751,6 @@
     }
 
     /**
-     * @hide
-     * Return if this user is a system-only user. An admin can manage a device from a system only
-     * user by calling {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE}.
-     * @param admin Which device owner this request is associated with.
-     * @return if this user is a system-only user.
-     */
-    public boolean isSystemOnlyUser(@NonNull ComponentName admin) {
-        try {
-            return mService.isSystemOnlyUser(admin);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Called by device owner, or profile owner on organization-owned device, to get the MAC
      * address of the Wi-Fi device.
      *
@@ -12007,6 +11992,9 @@
      * </ul>
      * Common Criteria mode is disabled by default.
      *
+     * <p><em>Note:</em> if Common Critera mode is turned off after being enabled previously,
+     * all existing WiFi configurations will be lost.
+     *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @param enabled whether Common Criteria mode should be enabled or not.
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9c6a274..1c7b617 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -345,7 +345,6 @@
     void setKeepUninstalledPackages(in ComponentName admin, in String callerPackage, in List<String> packageList);
     List<String> getKeepUninstalledPackages(in ComponentName admin, in String callerPackage);
     boolean isManagedProfile(in ComponentName admin);
-    boolean isSystemOnlyUser(in ComponentName admin);
     String getWifiMacAddress(in ComponentName admin);
     void reboot(in ComponentName admin);
 
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 22d8c87..b5234f8 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -36,8 +36,6 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -1676,8 +1674,9 @@
         }
 
         /**
-         * Returns the maximum length of the text associated with this node node, or {@code -1}
-         * if not supported by the node or not set.
+         * Returns the maximum length of the text associated with this node, or {@code -1} if not
+         * supported by the node or not set. System may set a default value if the text length is
+         * not set.
          *
          * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
          * not for assist purposes.
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index a34be5c..56bf59b 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -185,6 +185,9 @@
         final ActivityLifecycleItem lifecycleItem;
         switch (prevState) {
             // TODO(lifecycler): Extend to support all possible states.
+            case ON_START:
+                lifecycleItem = StartActivityItem.obtain();
+                break;
             case ON_PAUSE:
                 lifecycleItem = PauseActivityItem.obtain();
                 break;
diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING
new file mode 100644
index 0000000..46f2319
--- /dev/null
+++ b/core/java/android/app/timezonedetector/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.app.timezonedetector."
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
index 95db0a2..e879091 100644
--- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
+++ b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
@@ -162,9 +162,9 @@
 
     @Override
     public String toString() {
-        return "TimeZoneDetectorConfiguration{"
+        return "TimeZoneConfiguration{"
                 + "mUserId=" + mUserId
-                + "mBundle=" + mBundle
+                + ", mBundle=" + mBundle
                 + '}';
     }
 
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0e6a063..3522b1b 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -199,7 +199,7 @@
         public static final int NOTIFICATION_INTERRUPTION = 12;
 
         /**
-         * A Slice was pinned by the default assistant.
+         * A Slice was pinned by the default launcher or the default assistant.
          * @hide
          */
         @SystemApi
diff --git a/core/java/android/companion/TEST_MAPPING b/core/java/android/companion/TEST_MAPPING
new file mode 100644
index 0000000..63f54fa
--- /dev/null
+++ b/core/java/android/companion/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "include-filter": "android.os.cts.CompanionDeviceManagerTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 52b0467..98f7887 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4195,6 +4195,17 @@
     public static final String AUDIO_SERVICE = "audio";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a {@link
+     * android.media.MediaTranscodeManager} for transcoding media.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     * @see android.media.MediaTranscodeManager
+     */
+    @SystemApi
+    public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
+
+    /**
      * AuthService orchestrates biometric and PIN/pattern/password authentication.
      *
      * BiometricService was split into two services, AuthService and BiometricService, where
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/core/java/android/content/pm/FileChecksum.aidl
similarity index 76%
copy from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
copy to core/java/android/content/pm/FileChecksum.aidl
index 9d05843..109f211 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/core/java/android/content/pm/FileChecksum.aidl
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.settings
+package android.content.pm;
 
-import android.content.ContentResolver
+parcelable FileChecksum;
 
-interface CurrentUserContentResolverProvider {
-
-    val currentUserContentResolver: ContentResolver
-}
\ No newline at end of file
diff --git a/core/java/android/content/pm/FileChecksum.java b/core/java/android/content/pm/FileChecksum.java
new file mode 100644
index 0000000..55430c2
--- /dev/null
+++ b/core/java/android/content/pm/FileChecksum.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2020 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * A typed checksum.
+ *
+ * @see PackageManager#getChecksums(String, boolean, int, List, IntentSender)
+ */
+@DataClass(genHiddenConstructor = true)
+public final class FileChecksum implements Parcelable {
+    /**
+     * Checksum for which split. Null indicates base.apk.
+     */
+    private final @Nullable String mSplitName;
+    /**
+     * Checksum kind.
+     */
+    private final @PackageManager.FileChecksumKind int mKind;
+    /**
+     * Checksum value.
+     */
+    private final @NonNull byte[] mValue;
+    /**
+     * For Installer-provided checksums, certificate of the Installer/AppStore.
+     */
+    private final @Nullable byte[] mSourceCertificate;
+
+    /**
+     * Constructor, internal use only
+     *
+     * @hide
+     */
+    public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value) {
+        this(splitName, kind, value, (byte[]) null);
+    }
+
+    /**
+     * Constructor, internal use only
+     *
+     * @hide
+     */
+    public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value, @Nullable Certificate sourceCertificate)
+            throws CertificateEncodingException {
+        this(splitName, kind, value,
+                (sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
+    }
+
+    /**
+     * Certificate of the source of this checksum.
+     * @throws CertificateException in case when certificate can't be re-created from serialized
+     * data.
+     */
+    public @Nullable Certificate getSourceCertificate() throws CertificateException {
+        if (mSourceCertificate == null) {
+            return null;
+        }
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        final InputStream is = new ByteArrayInputStream(mSourceCertificate);
+        final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+        return cert;
+    }
+
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/FileChecksum.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new FileChecksum.
+     *
+     * @param splitName
+     *   Checksum for which split. Null indicates base.apk.
+     * @param kind
+     *   Checksum kind.
+     * @param value
+     *   Checksum value.
+     * @param sourceCertificate
+     *   For Installer-provided checksums, certificate of the Installer/AppStore.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public FileChecksum(
+            @Nullable String splitName,
+            @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value,
+            @Nullable byte[] sourceCertificate) {
+        this.mSplitName = splitName;
+        this.mKind = kind;
+        com.android.internal.util.AnnotationValidations.validate(
+                PackageManager.FileChecksumKind.class, null, mKind);
+        this.mValue = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mValue);
+        this.mSourceCertificate = sourceCertificate;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Checksum for which split. Null indicates base.apk.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getSplitName() {
+        return mSplitName;
+    }
+
+    /**
+     * Checksum kind.
+     */
+    @DataClass.Generated.Member
+    public @PackageManager.FileChecksumKind int getKind() {
+        return mKind;
+    }
+
+    /**
+     * Checksum value.
+     */
+    @DataClass.Generated.Member
+    public @NonNull byte[] getValue() {
+        return mValue;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mSplitName != null) flg |= 0x1;
+        if (mSourceCertificate != null) flg |= 0x8;
+        dest.writeByte(flg);
+        if (mSplitName != null) dest.writeString(mSplitName);
+        dest.writeInt(mKind);
+        dest.writeByteArray(mValue);
+        if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ FileChecksum(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String splitName = (flg & 0x1) == 0 ? null : in.readString();
+        int kind = in.readInt();
+        byte[] value = in.createByteArray();
+        byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
+
+        this.mSplitName = splitName;
+        this.mKind = kind;
+        com.android.internal.util.AnnotationValidations.validate(
+                PackageManager.FileChecksumKind.class, null, mKind);
+        this.mValue = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mValue);
+        this.mSourceCertificate = sourceCertificate;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<FileChecksum> CREATOR
+            = new Parcelable.Creator<FileChecksum>() {
+        @Override
+        public FileChecksum[] newArray(int size) {
+            return new FileChecksum[size];
+        }
+
+        @Override
+        public FileChecksum createFromParcel(@NonNull Parcel in) {
+            return new FileChecksum(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1598322801861L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/content/pm/FileChecksum.java",
+            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.content.pm.PackageManager.FileChecksumKind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass FileChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6a8dd81..1f8cee2 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -743,6 +743,8 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
+    void getChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+
     //------------------------------------------------------------------------
     //
     // The following binder interfaces have been moved to IPermissionManager
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index b7c3289..e6ea044 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2512,12 +2512,16 @@
         }
 
         /**
-         * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code true},
-         * return true. If it was called with {@code false} or if it was not called return false.
+         * Get if this session is to be installed as Instant Apps.
          *
-         * @hide
+         * @param isInstantApp an unused parameter and is ignored.
+         * @return {@code true} if {@link SessionParams#setInstallAsInstantApp(boolean)} was called
+         * with {@code true}; {@code false} if it was called with {@code false} or if it was not
+         * called.
          *
          * @see #getInstallAsFullApp
+         *
+         * @hide
          */
         @SystemApi
         public boolean getInstallAsInstantApp(boolean isInstantApp) {
@@ -2525,12 +2529,16 @@
         }
 
         /**
-         * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code false},
-         * return true. If it was called with {@code true} or if it was not called return false.
+         * Get if this session is to be installed as full apps.
          *
-         * @hide
+         * @param isInstantApp an unused parameter and is ignored.
+         * @return {@code true} if {@link SessionParams#setInstallAsInstantApp(boolean)} was called
+         * with {@code false}; {code false} if it was called with {@code true} or if it was not
+         * called.
          *
          * @see #getInstallAsInstantApp
+         *
+         * @hide
          */
         @SystemApi
         public boolean getInstallAsFullApp(boolean isInstantApp) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7b2955d..d2395ec 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -79,8 +79,11 @@
 import dalvik.system.VMRuntime;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -785,7 +788,6 @@
             INSTALL_ENABLE_ROLLBACK,
             INSTALL_ALLOW_DOWNGRADE,
             INSTALL_STAGED,
-            INSTALL_DRY_RUN,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InstallFlags {}
@@ -963,14 +965,6 @@
      */
     public static final int INSTALL_STAGED = 0x00200000;
 
-    /**
-     * Flag parameter for {@link #installPackage} to indicate that package should only be verified
-     * but not installed.
-     *
-     * @hide
-     */
-    public static final int INSTALL_DRY_RUN = 0x00800000;
-
     /** @hide */
     @IntDef(flag = true, value = {
             DONT_KILL_APP,
@@ -1161,7 +1155,8 @@
 
     /**
      * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
-     * if the new package uses a shared library that is not available.
+     * when the package being replaced is a system app and the caller didn't provide the
+     * {@link #DELETE_SYSTEM_APP} flag.
      *
      * @hide
      */
@@ -3305,6 +3300,13 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
+    /**
+     * Extra field name for the ID of a package pending verification. Passed to
+     * a package verifier and is used to call back to
+     * @see #getChecksums
+     */
+    public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
+
    /**
     * Permission flag: The permission is set in its current state
     * by the user and apps can still request it at runtime.
@@ -7842,6 +7844,114 @@
     }
 
     /**
+     * Root SHA256 hash of a 4K Merkle tree computed over all file bytes.
+     * <a href="https://source.android.com/security/apksigning/v4">See APK Signature Scheme V4</a>.
+     * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst">See fs-verity</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
+
+    /**
+     * MD5 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_MD5 = 0x00000002;
+
+    /**
+     * SHA1 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA1 = 0x00000004;
+
+    /**
+     * SHA256 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA256 = 0x00000008;
+
+    /**
+     * SHA512 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA512 = 0x00000010;
+
+    /**
+     * Root SHA256 hash of a 1M Merkle tree computed over protected content.
+     * Excludes signing block.
+     * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
+
+    /**
+     * Root SHA512 hash of a 1M Merkle tree computed over protected content.
+     * Excludes signing block.
+     * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = {
+            WHOLE_MERKLE_ROOT_4K_SHA256,
+            WHOLE_MD5,
+            WHOLE_SHA1,
+            WHOLE_SHA256,
+            WHOLE_SHA512,
+            PARTIAL_MERKLE_ROOT_1M_SHA256,
+            PARTIAL_MERKLE_ROOT_1M_SHA512,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FileChecksumKind {}
+
+    /**
+     * Trust any Installer to provide checksums for the package.
+     * @see #getChecksums
+     */
+    public static final @Nullable List<Certificate> TRUST_ALL = null;
+
+    /**
+     * Don't trust any Installer to provide checksums for the package.
+     * This effectively disables optimized Installer-enforced checksums.
+     * @see #getChecksums
+     */
+    public static final @NonNull List<Certificate> TRUST_NONE = Collections.emptyList();
+
+    /**
+     * Returns the checksums for APKs within a package.
+     *
+     * By default returns all readily available checksums:
+     * - enforced by platform,
+     * - enforced by installer.
+     * If caller needs a specific checksum kind, they can specify it as required.
+     *
+     * @param packageName whose checksums to return.
+     * @param includeSplits whether to include checksums for non-base splits.
+     * @param required explicitly request the checksum kinds. Will incur significant
+     *                 CPU/memory/disk usage.
+     * @param trustedInstallers for checksums enforced by Installer, which ones to be trusted.
+     *                          {@link #TRUST_ALL} will return checksums from any Installer,
+     *                          {@link #TRUST_NONE} disables optimized Installer-enforced checksums.
+     * @param statusReceiver called once when the results are available as
+     *                       {@link #EXTRA_CHECKSUMS} of type FileChecksum[].
+     * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
+     * @throws NameNotFoundException if a package with the given name cannot be found on the system.
+     */
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+            @NonNull IntentSender statusReceiver)
+            throws CertificateEncodingException, IOException, NameNotFoundException {
+        throw new UnsupportedOperationException("getChecksums not implemented in subclass");
+    }
+
+    /**
      * @return the default text classifier package name, or null if there's none.
      *
      * @hide
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0f1f276..e108451 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -30,7 +30,6 @@
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -159,8 +158,6 @@
             SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
 
     public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
-    public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
-    public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
 
     private static final int DEFAULT_MIN_SDK_VERSION = 1;
     private static final int DEFAULT_TARGET_SDK_VERSION = 0;
@@ -4683,20 +4680,8 @@
      * ratio set.
      */
     private void setMinAspectRatio(Package owner) {
-        final float minAspectRatio;
-        if (owner.applicationInfo.minAspectRatio != 0) {
-            // Use the application max aspect ration as default if set.
-            minAspectRatio = owner.applicationInfo.minAspectRatio;
-        } else {
-            // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
-            // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
-            // except for watches which always supported 1:1.
-            minAspectRatio = owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q
-                    ? 0
-                    : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH))
-                            ? DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
-                            : DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
-        }
+        // Use the application max aspect ration as default if set.
+        final float minAspectRatio = owner.applicationInfo.minAspectRatio;
 
         for (Activity activity : owner.activities) {
             if (activity.hasMinAspectRatio()) {
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index bd909c7..192470e 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -43,11 +43,11 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
-import libcore.io.IoUtils;
-
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -793,7 +793,7 @@
 
     @VisibleForTesting
     protected List<UserInfo> getUsers() {
-        return UserManager.get(mContext).getUsers(true);
+        return UserManager.get(mContext).getAliveUsers();
     }
 
     @VisibleForTesting
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index f8cabf7..6d92d3e 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -67,7 +67,7 @@
      * Automatically detects if the package is a monolithic style (single APK
      * file) or cluster style (directory of APKs).
      * <p>
-     * This performs sanity checking on cluster style packages, such as
+     * This performs validity checking on cluster style packages, such as
      * requiring identical package name and version codes, a single base APK,
      * and unique split names.
      *
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index f716ee1..741e80c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -18,7 +18,6 @@
 
 import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
@@ -211,7 +210,7 @@
      * package is a monolithic style (single APK file) or cluster style
      * (directory of APKs).
      * <p>
-     * This performs sanity checking on cluster style packages, such as
+     * This performs validity checking on cluster style packages, such as
      * requiring identical package name and version codes, a single base APK,
      * and unique split names.
      * <p>
@@ -237,7 +236,7 @@
 
     /**
      * Parse all APKs contained in the given directory, treating them as a
-     * single package. This also performs sanity checking, such as requiring
+     * single package. This also performs validity checking, such as requiring
      * identical package name and version codes, a single base APK, and unique
      * split names.
      * <p>
@@ -2370,21 +2369,8 @@
      * ratio set.
      */
     private void setMinAspectRatio(ParsingPackage pkg) {
-        final float minAspectRatio;
-        float packageMinAspectRatio = pkg.getMinAspectRatio();
-        if (packageMinAspectRatio != 0) {
-            // Use the application max aspect ration as default if set.
-            minAspectRatio = packageMinAspectRatio;
-        } else {
-            // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
-            // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
-            // except for watches which always supported 1:1.
-            minAspectRatio = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.Q
-                    ? 0
-                    : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH))
-                            ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
-                            : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
-        }
+        // Use the application max aspect ration as default if set.
+        final float minAspectRatio = pkg.getMinAspectRatio();
 
         List<ParsedActivity> activities = pkg.getActivities();
         int activitiesSize = activities.size();
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7250801..55afefe 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1210,15 +1210,19 @@
         mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
 
         // IME layout should always be inset by navigation bar, no matter its current visibility,
-        // unless automotive requests it, since automotive may hide the navigation bar.
+        // unless automotive requests it. Automotive devices may request the navigation bar to be
+        // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard)
+        // in order to maximize the visible screen real estate. When this happens, the IME window
+        // should animate from the bottom of the screen to reduce the jank that happens from the
+        // lack of synchronization between the bottom system window and the IME window.
+        if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
+            mWindow.getWindow().setDecorFitsSystemWindows(false);
+        }
         mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
                 (v, insets) -> v.onApplyWindowInsets(
                         new WindowInsets.Builder(insets).setInsets(
                                 navigationBars(),
-                                mIsAutomotive && mAutomotiveHideNavBarForKeyboard
-                                        ? android.graphics.Insets.NONE
-                                        : insets.getInsetsIgnoringVisibility(navigationBars())
-                                )
+                                insets.getInsetsIgnoringVisibility(navigationBars()))
                                 .build()));
 
         // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
diff --git a/core/java/android/inputmethodservice/TEST_MAPPING b/core/java/android/inputmethodservice/TEST_MAPPING
new file mode 100644
index 0000000..0ccd75d
--- /dev/null
+++ b/core/java/android/inputmethodservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+    {
+      "path": "frameworks/base/core/java/android/view/inputmethod"
+    }
+  ]
+}
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index 46aef10..a7dce18 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -85,6 +85,12 @@
     public static final int ERROR_INVALID_SOCKET = -25;
     /** The target socket is not idle. */
     public static final int ERROR_SOCKET_NOT_IDLE = -26;
+    /**
+     * The stop reason is uninitialized. This should only be internally used as initial state
+     * of stop reason, instead of propagating to application.
+     * @hide
+     */
+    public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
 
     /** The device does not support this request. */
     public static final int ERROR_UNSUPPORTED = -30;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 515704c..bec96f9 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1102,7 +1102,6 @@
     }
 
     private static native long getNativeBBinderHolder();
-    private static native long getFinalizer();
 
     /**
      * By default, we use the calling uid since we can always trust it.
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 683993f..0185ba4 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -255,7 +256,12 @@
             // out of system_server to all processes hosting binder objects it holds a reference to;
             // since some of those processes might be frozen, we don't want to block here
             // forever. Disable the freezer.
-            Process.enableFreezer(false);
+            try {
+                ActivityManager.getService().enableAppFreezer(false);
+            } catch (RemoteException e) {
+                Log.e(Binder.TAG, "RemoteException while disabling app freezer");
+            }
+
             for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
                 BinderProxy bp = weakRef.get();
                 String key;
@@ -278,7 +284,11 @@
                     counts.put(key, i + 1);
                 }
             }
-            Process.enableFreezer(true);
+            try {
+                ActivityManager.getService().enableAppFreezer(true);
+            } catch (RemoteException e) {
+                Log.e(Binder.TAG, "RemoteException while re-enabling app freezer");
+            }
             Map.Entry<String, Integer>[] sorted = counts.entrySet().toArray(
                     new Map.Entry[counts.size()]);
 
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index ab4bb0b..9c0bc45 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -25,6 +25,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
@@ -151,18 +152,18 @@
     /**
      * Creates a new {@link LocaleList}.
      *
+     * If two or more same locales are passed, the repeated locales will be dropped.
      * <p>For empty lists of {@link Locale} items it is better to use {@link #getEmptyLocaleList()},
      * which returns a pre-constructed empty list.</p>
      *
      * @throws NullPointerException if any of the input locales is <code>null</code>.
-     * @throws IllegalArgumentException if any of the input locales repeat.
      */
     public LocaleList(@NonNull Locale... list) {
         if (list.length == 0) {
             mList = sEmptyList;
             mStringRepresentation = "";
         } else {
-            final Locale[] localeList = new Locale[list.length];
+            final ArrayList<Locale> localeList = new ArrayList<>();
             final HashSet<Locale> seenLocales = new HashSet<Locale>();
             final StringBuilder sb = new StringBuilder();
             for (int i = 0; i < list.length; i++) {
@@ -170,10 +171,10 @@
                 if (l == null) {
                     throw new NullPointerException("list[" + i + "] is null");
                 } else if (seenLocales.contains(l)) {
-                    throw new IllegalArgumentException("list[" + i + "] is a repetition");
+                    // Dropping duplicated locale entries.
                 } else {
                     final Locale localeClone = (Locale) l.clone();
-                    localeList[i] = localeClone;
+                    localeList.add(localeClone);
                     sb.append(localeClone.toLanguageTag());
                     if (i < list.length - 1) {
                         sb.append(',');
@@ -181,7 +182,7 @@
                     seenLocales.add(localeClone);
                 }
             }
-            mList = localeList;
+            mList = localeList.toArray(new Locale[localeList.size()]);
             mStringRepresentation = sb.toString();
         }
     }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index a4077fb..efea953 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -947,7 +947,7 @@
 
     /**
      * Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
-     * but aren't removed from the freezer. Processes can still be added or removed
+     * but aren't removed from the freezer. While in this state, processes can be added or removed
      * by using setProcessFrozen, but they won't actually be frozen until the freezer is enabled
      * again. If enable == true the freezer is enabled again, and all processes
      * in the freezer (including the ones added while the freezer was disabled) are frozen.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2465b0e..81ffefd 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -43,7 +43,6 @@
 import android.content.IntentSender;
 import android.content.pm.UserInfo;
 import android.content.pm.UserInfo.UserInfoFlag;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -1294,7 +1293,7 @@
      * in {@link UserManager} & {@link DevicePolicyManager}.
      * Note: This is slightly different from the real set of user restrictions listed in {@link
      * com.android.server.pm.UserRestrictionsUtils#USER_RESTRICTIONS}. For example
-     * {@link #KEY_RESTRICTIONS_PENDING} is not a real user restriction, but is a a legitimate
+     * {@link #KEY_RESTRICTIONS_PENDING} is not a real user restriction, but is a legitimate
      * value that can be passed into {@link #hasUserRestriction(String)}.
      * @hide
      */
@@ -3174,28 +3173,55 @@
     }
 
     /**
-     * Returns information for all users on this device, including ones marked for deletion.
-     * To retrieve only users that are alive, use {@link #getUsers(boolean)}.
+     * Returns information for all fully-created users on this device, including ones marked for
+     * deletion.
      *
-     * @return the list of users that exist on the device.
+     * <p>To retrieve only users that are not marked for deletion, use {@link #getAliveUsers()}.
+     *
+     * <p>To retrieve *all* users (including partial and pre-created users), use
+     * {@link #getUsers(boolean, boolean, boolean)) getUsers(false, false, false)}.
+     *
+     * <p>To retrieve a more specific list of users, use
+     * {@link #getUsers(boolean, boolean, boolean)}.
+     *
+     * @return the list of users that were created.
+     *
      * @hide
      */
     @UnsupportedAppUsage
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public List<UserInfo> getUsers() {
-        return getUsers(/* excludeDying= */ false);
+        return getUsers(/*excludePartial= */ true, /* excludeDying= */ false,
+                /* excludePreCreated= */ true);
     }
 
     /**
-     * Returns information for all users on this device. Requires
-     * {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * Returns information for all "usable" users on this device (i.e, it excludes users that are
+     * marked for deletion, pre-created users, etc...).
      *
-     * @param excludeDying specify if the list should exclude users being
-     *            removed.
+     * <p>To retrieve all fully-created users, use {@link #getUsers()}.
+     *
+     * <p>To retrieve a more specific list of users, use
+     * {@link #getUsers(boolean, boolean, boolean)}.
+     *
      * @return the list of users that were created.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @NonNull List<UserInfo> getAliveUsers() {
+        return getUsers(/*excludePartial= */ true, /* excludeDying= */ true,
+                /* excludePreCreated= */ true);
+    }
+
+    /**
+     * @deprecated use {@link #getAliveUsers()} for {@code getUsers(true)}, or
+     * {@link #getUsers()} for @code getUsers(false)}.
+     *
+     * @hide
+     */
+    @Deprecated
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
         return getUsers(/*excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
@@ -3226,7 +3252,8 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) {
-        List<UserInfo> users = getUsers(excludeDying);
+        List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
+                /* excludePreCreated= */ true);
         List<UserHandle> result = new ArrayList<>(users.size());
         for (UserInfo user : users) {
             result.add(user.getUserHandle());
@@ -3244,7 +3271,8 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public long[] getSerialNumbersOfUsers(boolean excludeDying) {
-        List<UserInfo> users = getUsers(excludeDying);
+        List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
+                /* excludePreCreated= */ true);
         long[] result = new long[users.size()];
         for (int i = 0; i < result.length; i++) {
             result[i] = users.get(i).serialNumber;
@@ -3310,7 +3338,7 @@
     public boolean canAddMoreUsers() {
         // TODO(b/142482943): UMS has different logic, excluding Demo and Profile from counting. Why
         //                    not here? The logic is inconsistent. See UMS.canAddMoreManagedProfiles
-        final List<UserInfo> users = getUsers(true);
+        final List<UserInfo> users = getAliveUsers();
         final int totalUserCount = users.size();
         int aliveUserCount = 0;
         for (int i = 0; i < totalUserCount; i++) {
@@ -4118,7 +4146,7 @@
 
     /** Returns whether there are any users (other than the current user) to which to switch. */
     private boolean areThereUsersToWhichToSwitch() {
-        final List<UserInfo> users = getUsers(true);
+        final List<UserInfo> users = getAliveUsers();
         if (users == null) {
             return false;
         }
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 17851ad..7f01cad 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -155,6 +155,19 @@
         }
     }
     /**
+     * Complete the current partition installation.
+     *
+     * @return true if the partition installation completes without error.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public boolean closePartition() {
+        try {
+            return mService.closePartition();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+    /**
      * Finish a previously started installation. Installations without a cooresponding
      * finishInstallation() will be cleaned up during device boot.
      */
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index a1f9272..df0a69b 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -39,6 +39,13 @@
     boolean createPartition(@utf8InCpp String name, long size, boolean readOnly);
 
     /**
+     * Complete the current partition installation.
+     *
+     * @return true if the partition installation completes without error.
+     */
+    boolean closePartition();
+
+    /**
      * Finish a previously started installation. Installations without
      * a cooresponding finishInstallation() will be cleaned up during device boot.
      */
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index be8b929..f351c7d 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -90,6 +90,14 @@
     int unlink(int storageId, in @utf8InCpp String path);
 
     /**
+     * Checks if a file is fully loaded. File is specified by its path.
+     * 0 - fully loaded
+     * >0 - certain pages missing
+     * <0 - -errcode
+     */
+    int isFileFullyLoaded(int storageId, in @utf8InCpp String path);
+
+    /**
      * Returns overall loading progress of all the files on a storage, progress value between [0,1].
      * Returns a negative value on error.
      */
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index b8dbfbb..ed386f7 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -304,6 +304,25 @@
     }
 
     /**
+     * Checks whether a file under the current storage directory is fully loaded.
+     *
+     * @param path The relative path of the file.
+     * @return True if the file is fully loaded.
+     */
+    public boolean isFileFullyLoaded(@NonNull String path) throws IOException {
+        try {
+            int res = mService.isFileFullyLoaded(mId, path);
+            if (res < 0) {
+                throw new IOException("isFileFullyLoaded() failed, errno " + -res);
+            }
+            return res == 0;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
      * Returns the loading progress of a storage
      *
      * @return progress value between [0, 1].
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 276f162..c3b6d8e 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -870,7 +870,7 @@
 
                 // Otherwise, insert to all other users that are running and unlocked.
 
-                final List<UserInfo> users = userManager.getUsers(true);
+                final List<UserInfo> users = userManager.getAliveUsers();
 
                 final int count = users.size();
                 for (int i = 0; i < count; i++) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 859b703..7a03953 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -145,6 +145,15 @@
     public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
 
     /**
+     * Namespace for device idle configurations.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
+
+    /**
      * Namespace for how dex runs. The feature requires a reboot to reach a clean state.
      *
      * @deprecated No longer used
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 327bca2..4e1f819 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -218,8 +218,15 @@
     }
 
     /** {@hide} */
-    private void enforceTree(Uri documentUri) {
-        if (isTreeUri(documentUri)) {
+    private void enforceTreeForExtraUris(Bundle extras) {
+        enforceTree(extras.getParcelable(DocumentsContract.EXTRA_URI));
+        enforceTree(extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI));
+        enforceTree(extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI));
+    }
+
+    /** {@hide} */
+    private void enforceTree(@Nullable Uri documentUri) {
+        if (documentUri != null && isTreeUri(documentUri)) {
             final String parent = getTreeDocumentId(documentUri);
             final String child = getDocumentId(documentUri);
             if (Objects.equals(parent, child)) {
@@ -232,6 +239,10 @@
         }
     }
 
+    private Uri validateIncomingNullableUri(@Nullable Uri uri) {
+        return uri == null ? null : validateIncomingUri(uri);
+    }
+
     /**
      * Create a new document and return its newly generated
      * {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new
@@ -1076,11 +1087,21 @@
         final Context context = getContext();
         final Bundle out = new Bundle();
 
+        // If the URI is a tree URI performs some validation.
+        enforceTreeForExtraUris(extras);
+
+        final Uri extraUri = validateIncomingNullableUri(
+                extras.getParcelable(DocumentsContract.EXTRA_URI));
+        final Uri extraTargetUri = validateIncomingNullableUri(
+                extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI));
+        final Uri extraParentUri = validateIncomingNullableUri(
+                extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI));
+
         if (METHOD_EJECT_ROOT.equals(method)) {
             // Given that certain system apps can hold MOUNT_UNMOUNT permission, but only apps
             // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
             // MANAGE_DOCUMENTS or associated URI permission here instead
-            final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
+            final Uri rootUri = extraUri;
             enforceWritePermissionInner(rootUri, getCallingPackage(), getCallingAttributionTag(),
                     null);
 
@@ -1090,7 +1111,7 @@
             return out;
         }
 
-        final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
+        final Uri documentUri = extraUri;
         final String authority = documentUri.getAuthority();
         final String documentId = DocumentsContract.getDocumentId(documentUri);
 
@@ -1099,14 +1120,11 @@
                     "Requested authority " + authority + " doesn't match provider " + mAuthority);
         }
 
-        // If the URI is a tree URI performs some validation.
-        enforceTree(documentUri);
-
         if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
             enforceReadPermissionInner(documentUri, getCallingPackage(),
                     getCallingAttributionTag(), null);
 
-            final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
+            final Uri childUri = extraTargetUri;
             final String childAuthority = childUri.getAuthority();
             final String childId = DocumentsContract.getDocumentId(childUri);
 
@@ -1173,7 +1191,7 @@
             revokeDocumentPermission(documentId);
 
         } else if (METHOD_COPY_DOCUMENT.equals(method)) {
-            final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
+            final Uri targetUri = extraTargetUri;
             final String targetId = DocumentsContract.getDocumentId(targetUri);
 
             enforceReadPermissionInner(documentUri, getCallingPackage(),
@@ -1197,9 +1215,9 @@
             }
 
         } else if (METHOD_MOVE_DOCUMENT.equals(method)) {
-            final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
+            final Uri parentSourceUri = extraParentUri;
             final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
-            final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
+            final Uri targetUri = extraTargetUri;
             final String targetId = DocumentsContract.getDocumentId(targetUri);
 
             enforceWritePermissionInner(documentUri, getCallingPackage(),
@@ -1225,7 +1243,7 @@
             }
 
         } else if (METHOD_REMOVE_DOCUMENT.equals(method)) {
-            final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
+            final Uri parentSourceUri = extraParentUri;
             final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
 
             enforceReadPermissionInner(parentSourceUri, getCallingPackage(),
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 03cf0cf..739e169 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11534,38 +11534,6 @@
         public static final String APP_OPS_CONSTANTS = "app_ops_constants";
 
         /**
-         * Device Idle (Doze) specific settings.
-         * This is encoded as a key=value list, separated by commas. Ex:
-         *
-         * "inactive_to=60000,sensing_to=400000"
-         *
-         * The following keys are supported:
-         *
-         * <pre>
-         * inactive_to                      (long)
-         * sensing_to                       (long)
-         * motion_inactive_to               (long)
-         * idle_after_inactive_to           (long)
-         * idle_pending_to                  (long)
-         * max_idle_pending_to              (long)
-         * idle_pending_factor              (float)
-         * quick_doze_delay_to              (long)
-         * idle_to                          (long)
-         * max_idle_to                      (long)
-         * idle_factor                      (float)
-         * min_time_to_alarm                (long)
-         * max_temp_app_whitelist_duration  (long)
-         * notification_whitelist_duration  (long)
-         * </pre>
-         *
-         * <p>
-         * Type: string
-         * @hide
-         * @see com.android.server.DeviceIdleController.Constants
-         */
-        public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
-
-        /**
          * Battery Saver specific settings
          * This is encoded as a key=value list, separated by commas. Ex:
          *
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index b807180..8f31d77 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -486,6 +486,16 @@
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_BARRING_INFO = 0x80000000;
 
+    /**
+     *  Listen for changes to the physical channel configuration.
+     *
+     * @see #onPhysicalChannelConfigurationChanged
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+    public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x100000000L;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -950,10 +960,6 @@
      * This method will be called when an emergency call is placed on any subscription (including
      * the no-SIM case), regardless of which subscription this listener was registered on.
      *
-     * This method is deprecated. Both this method and the new
-     * {@link #onOutgoingEmergencyCall(EmergencyNumber, int)} will be called when an outgoing
-     * emergency call is placed.
-     *
      * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
      *
      * @deprecated Use {@link #onOutgoingEmergencyCall(EmergencyNumber, int)}.
@@ -972,22 +978,24 @@
      * This method will be called when an emergency call is placed on any subscription (including
      * the no-SIM case), regardless of which subscription this listener was registered on.
      *
-     * Both this method and the deprecated {@link #onOutgoingEmergencyCall(EmergencyNumber)} will be
-     * called when an outgoing emergency call is placed. You should only implement one of these
-     * methods.
+     * The default implementation of this method calls
+     * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes. Do
+     * not call {@code super(...)} from within your implementation unless you want
+     * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well.
      *
      * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
      * @param subscriptionId The subscription ID used to place the emergency call. If the
      *                       emergency call was placed without a valid subscription (e.g. when there
      *                       are no SIM cards in the device), this will be equal to
      *                       {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
-     *
      * @hide
      */
     @SystemApi
     @TestApi
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
             int subscriptionId) {
+        // Default implementation for backwards compatibility
+        onOutgoingEmergencyCall(placedEmergencyNumber);
     }
 
     /**
@@ -1147,6 +1155,18 @@
     }
 
     /**
+     * Callback invoked when the current physical channel configuration has changed
+     *
+     * @param configs List of the current {@link PhysicalChannelConfig}s
+     * @hide
+     */
+    @SystemApi
+    public void onPhysicalChannelConfigurationChanged(
+            @NonNull List<PhysicalChannelConfig> configs) {
+        // default implementation empty
+    }
+
+    /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
      *
@@ -1375,10 +1395,6 @@
 
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
-                            () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber)));
-
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(
                             () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber,
                                     subscriptionId)));
         }
@@ -1453,6 +1469,15 @@
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo)));
         }
+
+        public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onPhysicalChannelConfigurationChanged(configs)));
+        }
     }
 
 
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index a720601..6d48dc3 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -211,7 +211,7 @@
      * @param notifyNow Whether to notify instantly
      */
     public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId,
-            @NonNull PhoneStateListener listener, int events, boolean notifyNow) {
+            @NonNull PhoneStateListener listener, long events, boolean notifyNow) {
         try {
             // subId from PhoneStateListener is deprecated Q on forward, use the subId from
             // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
@@ -754,4 +754,19 @@
         }
     }
 
+    /**
+     * Notify {@link PhysicalChannelConfig} has changed for a specific subscription.
+     *
+     * @param subId the subId
+     * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
+     */
+    public void notifyPhysicalChannelConfigurationForSubscriber(
+            int subId, List<PhysicalChannelConfig> configs) {
+        try {
+            sRegistry.notifyPhysicalChannelConfigurationForSubscriber(subId, configs);
+        } catch (RemoteException ex) {
+            // system server crash
+        }
+    }
+
 }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 6e34666..f74990a 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -149,7 +149,7 @@
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    private static SignatureInfo findSignature(RandomAccessFile apk)
+    public static SignatureInfo findSignature(RandomAccessFile apk)
             throws IOException, SignatureNotFoundException {
         return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 9357285..5f963b0 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -142,7 +142,7 @@
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    private static SignatureInfo findSignature(RandomAccessFile apk)
+    public static SignatureInfo findSignature(RandomAccessFile apk)
             throws IOException, SignatureNotFoundException {
         return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
     }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index e0258f7..02edb7e 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -92,6 +92,20 @@
     private static PackageParser.SigningDetails verifySignatures(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws PackageParserException {
+        return verifySignaturesInternal(apkPath, minSignatureSchemeVersion,
+                verifyFull).signingDetails;
+    }
+
+    /**
+     * Verifies the provided APK using all allowed signing schemas.
+     * @return the certificates associated with each signer and content digests.
+     * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+     * @throws PackageParserException if there was a problem collecting certificates
+     * @hide
+     */
+    public static SigningDetailsWithDigests verifySignaturesInternal(String apkPath,
+            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
+            throws PackageParserException {
 
         if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
             // V3 and before are older than the requested minimum signing version
@@ -121,7 +135,7 @@
         return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull);
     }
 
-    private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath,
+    private static SigningDetailsWithDigests verifyV3AndBelowSignatures(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws PackageParserException {
         // try v3
@@ -174,7 +188,7 @@
      * @throws SignatureNotFoundException if there are no V4 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV4Signature(String apkPath,
+    private static SigningDetailsWithDigests verifyV4Signature(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
@@ -234,8 +248,8 @@
                 }
             }
 
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V4);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V4), vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -256,8 +270,8 @@
      * @throws SignatureNotFoundException if there are no V3 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
-            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+    private static SigningDetailsWithDigests verifyV3Signature(String apkPath, boolean verifyFull)
+            throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
         try {
             ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
@@ -275,8 +289,9 @@
                     pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
                 }
             }
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs),
+                    vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -297,15 +312,16 @@
      * @throws SignatureNotFoundException if there are no V2 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV2Signature(String apkPath,
-            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+    private static SigningDetailsWithDigests verifyV2Signature(String apkPath, boolean verifyFull)
+            throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
         try {
-            Certificate[][] signerCerts = verifyFull ? ApkSignatureSchemeV2Verifier.verify(apkPath)
-                    : ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
+            ApkSignatureSchemeV2Verifier.VerifiedSigner vSigner =
+                    ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull);
+            Certificate[][] signerCerts = vSigner.certs;
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V2);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -324,8 +340,7 @@
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
      * @throws PackageParserException if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV1Signature(
-            String apkPath, boolean verifyFull)
+    private static SigningDetailsWithDigests verifyV1Signature(String apkPath, boolean verifyFull)
             throws PackageParserException {
         StrictJarFile jarFile = null;
 
@@ -391,7 +406,8 @@
                     }
                 }
             }
-            return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
+            return new SigningDetailsWithDigests(
+                    new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
         } catch (GeneralSecurityException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to collect certificates from " + apkPath, e);
@@ -542,4 +558,27 @@
             return null;
         }
     }
+
+    /**
+     * Extended signing details.
+     * @hide for internal use only.
+     */
+    public static class SigningDetailsWithDigests {
+        public final PackageParser.SigningDetails signingDetails;
+
+        /**
+         * APK Signature Schemes v2/v3/v4 might contain multiple content digests.
+         * SignatureVerifier usually chooses one of them to verify.
+         * For certain signature schemes, e.g. v4, this digest is verified continuously.
+         * For others, e.g. v2, the caller has to specify if they want to verify.
+         * Please refer to documentation for more details.
+         */
+        public final Map<Integer, byte[]> contentDigests;
+
+        SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails,
+                Map<Integer, byte[]> contentDigests) {
+            this.signingDetails = signingDetails;
+            this.contentDigests = contentDigests;
+        }
+    }
 }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 990092c..021f232 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -39,7 +39,7 @@
  *
  * @hide for internal use only.
  */
-final class ApkSigningBlockUtils {
+public final class ApkSigningBlockUtils {
 
     private ApkSigningBlockUtils() {
     }
@@ -146,6 +146,37 @@
             Map<Integer, byte[]> expectedDigests,
             FileDescriptor apkFileDescriptor,
             SignatureInfo signatureInfo) throws SecurityException {
+        int[] digestAlgorithms = new int[expectedDigests.size()];
+        int digestAlgorithmCount = 0;
+        for (int digestAlgorithm : expectedDigests.keySet()) {
+            digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
+            digestAlgorithmCount++;
+        }
+        byte[][] actualDigests;
+        try {
+            actualDigests = computeContentDigestsPer1MbChunk(digestAlgorithms, apkFileDescriptor,
+                    signatureInfo);
+        } catch (DigestException e) {
+            throw new SecurityException("Failed to compute digest(s) of contents", e);
+        }
+        for (int i = 0; i < digestAlgorithms.length; i++) {
+            int digestAlgorithm = digestAlgorithms[i];
+            byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
+            byte[] actualDigest = actualDigests[i];
+            if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
+                throw new SecurityException(
+                        getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+                                + " digest of contents did not verify");
+            }
+        }
+    }
+
+    /**
+     * Calculate digests using digestAlgorithms for apkFileDescriptor.
+     * This will skip signature block described by signatureInfo.
+     */
+    public static byte[][] computeContentDigestsPer1MbChunk(int[] digestAlgorithms,
+            FileDescriptor apkFileDescriptor, SignatureInfo signatureInfo) throws DigestException {
         // We need to verify the integrity of the following three sections of the file:
         // 1. Everything up to the start of the APK Signing Block.
         // 2. ZIP Central Directory.
@@ -156,6 +187,7 @@
         // avoid wasting physical memory. In most APK verification scenarios, the contents of the
         // APK are already there in the OS's page cache and thus mmap does not use additional
         // physical memory.
+
         DataSource beforeApkSigningBlock =
                 new MemoryMappedFileDataSource(apkFileDescriptor, 0,
                         signatureInfo.apkSigningBlockOffset);
@@ -171,31 +203,8 @@
         ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset);
         DataSource eocd = new ByteBufferDataSource(eocdBuf);
 
-        int[] digestAlgorithms = new int[expectedDigests.size()];
-        int digestAlgorithmCount = 0;
-        for (int digestAlgorithm : expectedDigests.keySet()) {
-            digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
-            digestAlgorithmCount++;
-        }
-        byte[][] actualDigests;
-        try {
-            actualDigests =
-                    computeContentDigestsPer1MbChunk(
-                            digestAlgorithms,
-                            new DataSource[] {beforeApkSigningBlock, centralDir, eocd});
-        } catch (DigestException e) {
-            throw new SecurityException("Failed to compute digest(s) of contents", e);
-        }
-        for (int i = 0; i < digestAlgorithms.length; i++) {
-            int digestAlgorithm = digestAlgorithms[i];
-            byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
-            byte[] actualDigest = actualDigests[i];
-            if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
-                throw new SecurityException(
-                        getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
-                                + " digest of contents did not verify");
-            }
-        }
+        return computeContentDigestsPer1MbChunk(digestAlgorithms,
+                new DataSource[]{beforeApkSigningBlock, centralDir, eocd});
     }
 
     private static byte[][] computeContentDigestsPer1MbChunk(
@@ -417,14 +426,10 @@
     static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423;
     static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425;
 
-    static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
-    static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
-    static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
-    static final int CONTENT_DIGEST_SHA256 = 4;
-
-    private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
-            {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
-                    CONTENT_DIGEST_CHUNKED_SHA256};
+    public static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
+    public static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
+    public static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
+    public static final int CONTENT_DIGEST_SHA256 = 4;
 
     static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
         int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
diff --git a/core/java/android/util/apk/SignatureInfo.java b/core/java/android/util/apk/SignatureInfo.java
index 8e1233a..7638293 100644
--- a/core/java/android/util/apk/SignatureInfo.java
+++ b/core/java/android/util/apk/SignatureInfo.java
@@ -16,15 +16,18 @@
 
 package android.util.apk;
 
+import android.annotation.NonNull;
+
 import java.nio.ByteBuffer;
 
 /**
  * APK Signature Scheme v2 block and additional information relevant to verifying the signatures
  * contained in the block against the file.
+ * @hide
  */
-class SignatureInfo {
+public class SignatureInfo {
     /** Contents of APK Signature Scheme v2 block. */
-    public final ByteBuffer signatureBlock;
+    public final @NonNull ByteBuffer signatureBlock;
 
     /** Position of the APK Signing Block in the file. */
     public final long apkSigningBlockOffset;
@@ -36,10 +39,10 @@
     public final long eocdOffset;
 
     /** Contents of ZIP End of Central Directory (EoCD) of the file. */
-    public final ByteBuffer eocd;
+    public final @NonNull ByteBuffer eocd;
 
-    SignatureInfo(ByteBuffer signatureBlock, long apkSigningBlockOffset, long centralDirOffset,
-            long eocdOffset, ByteBuffer eocd) {
+    SignatureInfo(@NonNull ByteBuffer signatureBlock, long apkSigningBlockOffset,
+            long centralDirOffset, long eocdOffset, @NonNull ByteBuffer eocd) {
         this.signatureBlock = signatureBlock;
         this.apkSigningBlockOffset = apkSigningBlockOffset;
         this.centralDirOffset = centralDirOffset;
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index e81e3f7..4596c6e 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -116,6 +116,34 @@
     }
 
     /**
+     * Generates the fs-verity hash tree. It is the actual verity tree format on disk, as is
+     * re-generated on device.
+     *
+     * The tree is built bottom up. The bottom level has 256-bit digest for each 4 KB block in the
+     * input file.  If the total size is larger than 4 KB, take this level as input and repeat the
+     * same procedure, until the level is within 4 KB.  If salt is given, it will apply to each
+     * digestion before the actual data.
+     *
+     * The returned root hash is calculated from the last level of 4 KB chunk, similarly with salt.
+     *
+     * @return the root hash of the generated hash tree.
+     */
+    public static byte[] generateFsVerityRootHash(@NonNull String apkPath, byte[] salt,
+            @NonNull ByteBufferFactory bufferFactory)
+            throws IOException, NoSuchAlgorithmException, DigestException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            int[] levelOffset = calculateVerityLevelOffset(apk.length());
+            int merkleTreeSize = levelOffset[levelOffset.length - 1];
+
+            ByteBuffer output = bufferFactory.create(
+                    merkleTreeSize
+                            + CHUNK_SIZE_BYTES);  // maximum size of apk-verity metadata
+            output.order(ByteOrder.LITTLE_ENDIAN);
+            ByteBuffer tree = slice(output, 0, merkleTreeSize);
+            return generateFsVerityTreeInternal(apk, salt, levelOffset, tree);
+        }
+    }
+    /**
      * Calculates the apk-verity root hash for integrity measurement.  This needs to be consistent
      * to what kernel returns.
      */
@@ -259,9 +287,10 @@
     // thus the syscall overhead is not too big.
     private static final int MMAP_REGION_SIZE_BYTES = 1024 * 1024;
 
-    private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, ByteBuffer output)
+    private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file,
+            @Nullable byte[] salt, ByteBuffer output)
             throws IOException, NoSuchAlgorithmException, DigestException {
-        BufferedDigester digester = new BufferedDigester(null /* salt */, output);
+        BufferedDigester digester = new BufferedDigester(salt, output);
 
         // 1. Digest the whole file by chunks.
         consumeByChunk(digester,
@@ -325,6 +354,35 @@
     }
 
     @NonNull
+    private static byte[] generateFsVerityTreeInternal(@NonNull RandomAccessFile apk,
+            @Nullable byte[] salt, @NonNull int[] levelOffset, @NonNull ByteBuffer output)
+            throws IOException, NoSuchAlgorithmException, DigestException {
+        // 1. Digest the apk to generate the leaf level hashes.
+        generateFsVerityDigestAtLeafLevel(apk, salt,
+                slice(output, levelOffset[levelOffset.length - 2],
+                        levelOffset[levelOffset.length - 1]));
+
+        // 2. Digest the lower level hashes bottom up.
+        for (int level = levelOffset.length - 3; level >= 0; level--) {
+            ByteBuffer inputBuffer = slice(output, levelOffset[level + 1], levelOffset[level + 2]);
+            ByteBuffer outputBuffer = slice(output, levelOffset[level], levelOffset[level + 1]);
+
+            DataSource source = new ByteBufferDataSource(inputBuffer);
+            BufferedDigester digester = new BufferedDigester(salt, outputBuffer);
+            consumeByChunk(digester, source, CHUNK_SIZE_BYTES);
+            digester.assertEmptyBuffer();
+            digester.fillUpLastOutputChunk();
+        }
+
+        // 3. Digest the first block (i.e. first level) to generate the root hash.
+        byte[] rootHash = new byte[DIGEST_SIZE_BYTES];
+        BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash));
+        digester.consume(slice(output, 0, CHUNK_SIZE_BYTES));
+        digester.assertEmptyBuffer();
+        return rootHash;
+    }
+
+    @NonNull
     private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt,
             @NonNull int[] levelOffset, @NonNull ByteBuffer output)
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index 30e3ec1..7023e4b 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -24,7 +24,8 @@
  * @hide
  */
 public class BatchedInputEventReceiver extends InputEventReceiver {
-    Choreographer mChoreographer;
+    private Choreographer mChoreographer;
+    private boolean mBatchingEnabled;
     private boolean mBatchedInputScheduled;
 
     @UnsupportedAppUsage
@@ -32,19 +33,37 @@
             InputChannel inputChannel, Looper looper, Choreographer choreographer) {
         super(inputChannel, looper);
         mChoreographer = choreographer;
+        mBatchingEnabled = true;
     }
 
     @Override
     public void onBatchedInputEventPending(int source) {
-        scheduleBatchedInput();
+        if (mBatchingEnabled) {
+            scheduleBatchedInput();
+        } else {
+            consumeBatchedInputEvents(-1);
+        }
     }
 
     @Override
     public void dispose() {
         unscheduleBatchedInput();
+        consumeBatchedInputEvents(-1);
         super.dispose();
     }
 
+    /**
+     * Sets whether to enable batching on this input event receiver.
+     * @hide
+     */
+    public void setBatchingEnabled(boolean batchingEnabled) {
+        mBatchingEnabled = batchingEnabled;
+        if (!batchingEnabled) {
+            unscheduleBatchedInput();
+            consumeBatchedInputEvents(-1);
+        }
+    }
+
     void doConsumeBatchedInput(long frameTimeNanos) {
         if (mBatchedInputScheduled) {
             mBatchedInputScheduled = false;
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 054dff7..32cc30be8 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -250,8 +250,11 @@
         Index.INTENDED_VSYNC, Index.FRAME_COMPLETED,
     };
 
+    /**
+     * @hide
+     */
     @UnsupportedAppUsage
-    /* package */ final long[] mTimingData;
+    public final long[] mTimingData;
 
     /**
      * Constructs a FrameMetrics object as a copy.
@@ -270,7 +273,7 @@
     /**
      * @hide
      */
-    FrameMetrics() {
+    public FrameMetrics() {
         mTimingData = new long[Index.FRAME_STATS_COUNT];
     }
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 00fc672..8e875d7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -393,11 +393,6 @@
     oneway void setRecentsVisibility(boolean visible);
 
     /**
-     * Called by System UI to notify of changes to the visibility of PIP.
-     */
-    oneway void setPipVisibility(boolean visible);
-
-    /**
      * Called by System UI to enable or disable haptic feedback on the navigation bar buttons.
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6922072..6ffd892 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsController.AnimationType;
 import static android.view.InsetsController.DEBUG;
@@ -24,6 +25,7 @@
 import static android.view.InsetsState.ISIDE_RIGHT;
 import static android.view.InsetsState.ISIDE_TOP;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
@@ -308,8 +310,8 @@
                 false /* isScreenRound */,
                 false /* alwaysConsumeSystemBars */, null /* displayCutout */,
                 LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
-                0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, typeSideMap)
-               .getInsets(mTypes);
+                0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
+                WINDOWING_MODE_UNDEFINED, typeSideMap).getInsets(mTypes);
     }
 
     private Insets sanitize(Insets insets) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 403ac3a..92eade3 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -500,9 +500,11 @@
     /** Pending control request that is waiting on IME to be ready to be shown */
     private PendingControlRequest mPendingImeControlRequest;
 
+    private int mWindowType;
     private int mLastLegacySoftInputMode;
     private int mLastLegacyWindowFlags;
     private int mLastLegacySystemUiFlags;
+    private int mLastWindowingMode;
     private DisplayCutout mLastDisplayCutout;
     private boolean mStartingAnimation;
     private int mCaptionInsetsHeight = 0;
@@ -571,7 +573,8 @@
             WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
                     mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
                     mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacyWindowFlags,
-                    mLastLegacySystemUiFlags, null /* typeSideMap */);
+                    mLastLegacySystemUiFlags, mWindowType, mLastWindowingMode,
+                    null /* typeSideMap */);
             mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims);
             if (DEBUG) {
                 for (WindowInsetsAnimation anim : mUnmodifiableTmpRunningAnims) {
@@ -709,9 +712,11 @@
      * @see InsetsState#calculateInsets
      */
     @VisibleForTesting
-    public WindowInsets calculateInsets(boolean isScreenRound,
-            boolean alwaysConsumeSystemBars, DisplayCutout cutout,
+    public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars,
+            DisplayCutout cutout, int windowType, int windowingMode,
             int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) {
+        mWindowType = windowType;
+        mLastWindowingMode = windowingMode;
         mLastLegacySoftInputMode = legacySoftInputMode;
         mLastLegacyWindowFlags = legacyWindowFlags;
         mLastLegacySystemUiFlags = legacySystemUiFlags;
@@ -719,7 +724,7 @@
         mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
                 isScreenRound, alwaysConsumeSystemBars, cutout,
                 legacySoftInputMode, legacyWindowFlags, legacySystemUiFlags,
-                null /* typeSideMap */);
+                windowType, windowingMode, null /* typeSideMap */);
         return mLastInsets;
     }
 
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 700dc66..ba40459 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -113,13 +113,20 @@
                     InsetsState.typeToString(control.getType()),
                     mController.getHost().getRootViewTitle()));
         }
-        // We are loosing control
         if (mSourceControl == null) {
+            // We are loosing control
             mController.notifyControlRevoked(this);
 
-            // Restore server visibility.
-            mState.getSource(getType()).setVisible(
-                    mController.getLastDispatchedState().getSource(getType()).isVisible());
+            // Check if we need to restore server visibility.
+            final InsetsSource source = mState.getSource(mType);
+            final boolean serverVisibility =
+                    mController.getLastDispatchedState().getSourceOrDefaultVisibility(mType);
+            if (source.isVisible() != serverVisibility) {
+                source.setVisible(serverVisibility);
+                mController.notifyVisibilityChanged();
+            }
+
+            // For updateCompatSysUiVisibility
             applyLocalVisibilityOverride();
         } else {
             // We are gaining control, and need to run an animation since previous state
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 593b37a..373a096 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -30,11 +30,15 @@
 import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowInsets.Type.systemBars;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.app.WindowConfiguration;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Parcel;
@@ -174,6 +178,7 @@
     public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
             boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
             int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
+            int windowType, @WindowConfiguration.WindowingMode int windowingMode,
             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
         Insets[] typeInsetsMap = new Insets[Type.SIZE];
         Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
@@ -228,6 +233,9 @@
         if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
             compatInsetsTypes &= ~statusBars();
         }
+        if (clearCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
+            compatInsetsTypes = 0;
+        }
 
         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
                 alwaysConsumeSystemBars, cutout, compatInsetsTypes,
@@ -449,6 +457,12 @@
         mSources[source.getType()] = source;
     }
 
+    public static boolean clearCompatInsets(int windowType, int windowFlags, int windowingMode) {
+        return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0
+                && windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR
+                && !WindowConfiguration.inMultiWindowMode(windowingMode);
+    }
+
     public static @InternalInsetsType ArraySet<Integer> toInternalType(@InsetsType int types) {
         final ArraySet<Integer> result = new ArraySet<>();
         if ((types & Type.STATUS_BARS) != 0) {
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 6136a80..0c3d61f 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -456,8 +456,8 @@
                 case MotionEvent.ACTION_UP:
                     if (mTrackGesture) {
                         if (mFeedbackIcon.isVisibleToUser()
-                                && (mFeedbackRect.contains((int) x, (int) y))
-                                || mFeedbackRect.contains((int) mDownX, (int) mDownY)) {
+                                && (mFeedbackRect.contains((int) x, (int) y)
+                                || mFeedbackRect.contains((int) mDownX, (int) mDownY))) {
                             mFeedbackIcon.performClick();
                             return true;
                         }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6ef086b..67681da 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -65,6 +65,9 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is
@@ -87,10 +90,10 @@
     private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeDisconnect(long nativeObject);
-    private static native ScreenshotHardwareBuffer nativeCaptureDisplay(
-            DisplayCaptureArgs captureArgs);
-    private static native ScreenshotHardwareBuffer nativeCaptureLayers(
-            LayerCaptureArgs captureArgs);
+    private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
+            ScreenCaptureListener captureListener);
+    private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
+            ScreenCaptureListener captureListener);
     private static native long nativeMirrorSurface(long mirrorOfObject);
     private static native long nativeCreateTransaction();
     private static native long nativeGetNativeTransactionFinalizer();
@@ -221,6 +224,8 @@
     private static native void nativeReleaseFrameRateFlexibilityToken(long token);
     private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject,
             int transformHint);
+    private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
+                                                      IBinder focusedToken, int displayId);
 
     @Nullable
     @GuardedBy("mLock")
@@ -493,6 +498,8 @@
     private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
     private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
 
+    private static final int SCREENSHOT_WAIT_TIME_S = 1;
+
     private void assignNativeObject(long nativeObject, String callsite) {
         if (mNativeObject != 0) {
             release();
@@ -611,6 +618,13 @@
     }
 
     /**
+     * @hide
+     */
+    public abstract static class ScreenCaptureListener {
+        abstract void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
+    }
+
+    /**
      * A common arguments class used for various screenshot requests. This contains arguments that
      * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
      * @hide
@@ -685,7 +699,7 @@
     /**
      * The arguments class used to make display capture requests.
      *
-     * @see #nativeCaptureDisplay(DisplayCaptureArgs)
+     * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
      * @hide
      */
     public static class DisplayCaptureArgs extends CaptureArgs {
@@ -2226,13 +2240,46 @@
     }
 
     /**
+     * @param captureArgs Arguments about how to take the screenshot
+     * @param captureListener A listener to receive the screenshot callback
+     * @hide
+     */
+    public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
+            @NonNull ScreenCaptureListener captureListener) {
+        return nativeCaptureDisplay(captureArgs, captureListener);
+    }
+
+    /**
      * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
      * the content.
      *
      * @hide
      */
     public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) {
-        return nativeCaptureDisplay(captureArgs);
+        final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer =
+                new AtomicReference<>(null);
+
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() {
+            @Override
+            void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
+                outHardwareBuffer.set(hardwareBuffer);
+                countDownLatch.countDown();
+            }
+        };
+
+        int status = captureDisplay(captureArgs, screenCaptureListener);
+        if (status != 0) {
+            return null;
+        }
+
+        try {
+            countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to wait for captureDisplay result", e);
+        }
+
+        return outHardwareBuffer.get();
     }
 
     /**
@@ -2277,14 +2324,37 @@
                 .setPixelFormat(format)
                 .build();
 
-        return nativeCaptureLayers(captureArgs);
+        return captureLayers(captureArgs);
     }
 
     /**
      * @hide
      */
     public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
-        return nativeCaptureLayers(captureArgs);
+        final AtomicReference<ScreenshotHardwareBuffer> outHardwareBuffer =
+                new AtomicReference<>(null);
+
+        final CountDownLatch countDownLatch = new CountDownLatch(1);
+        ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener() {
+            @Override
+            void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
+                outHardwareBuffer.set(hardwareBuffer);
+                countDownLatch.countDown();
+            }
+        };
+
+        int status = captureLayers(captureArgs, screenCaptureListener);
+        if (status != 0) {
+            return null;
+        }
+
+        try {
+            countDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to wait for captureLayers result", e);
+        }
+
+        return outHardwareBuffer.get();
     }
 
     /**
@@ -2301,7 +2371,17 @@
                 .setExcludeLayers(exclude)
                 .build();
 
-        return nativeCaptureLayers(captureArgs);
+        return captureLayers(captureArgs);
+    }
+
+    /**
+     * @param captureArgs Arguments about how to take the screenshot
+     * @param captureListener A listener to receive the screenshot callback
+     * @hide
+     */
+    public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
+            @NonNull ScreenCaptureListener captureListener) {
+        return nativeCaptureLayers(captureArgs, captureListener);
     }
 
     /**
@@ -3184,6 +3264,39 @@
         }
 
         /**
+         * Sets focus on the window identified by the input {@code token} if the window is focusable
+         * otherwise the request is dropped.
+         *
+         * If the window is not visible, the request will be queued until the window becomes
+         * visible or the request is overrriden by another request. The currently focused window
+         * will lose focus immediately. This is to send the newly focused window any focus
+         * dispatched events that occur while it is completing its first draw.
+         *
+         * @hide
+         */
+        public Transaction setFocusedWindow(@NonNull IBinder token, int displayId) {
+            nativeSetFocusedWindow(mNativeObject, token,  null /* focusedToken */, displayId);
+            return this;
+        }
+
+        /**
+         * Set focus on the window identified by the input {@code token} if the window identified by
+         * the input {@code focusedToken} is currently focused. If the {@code focusedToken} does not
+         * have focus, the request is dropped.
+         *
+         * This is used by forward focus transfer requests from clients that host embedded windows,
+         * and want to transfer focus to/from them.
+         *
+         * @hide
+         */
+        public Transaction requestFocusTransfer(@NonNull IBinder token,
+                                                @NonNull IBinder focusedToken,
+                                                int displayId) {
+            nativeSetFocusedWindow(mNativeObject, token, focusedToken, displayId);
+            return this;
+        }
+
+         /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
          *
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ccf1fb0..abf76ec 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -100,7 +100,7 @@
      * Defines the duration in milliseconds a user needs to hold down the
      * appropriate buttons (power + volume down) to trigger the screenshot chord.
      */
-    private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 500;
+    private static final int SCREENSHOT_CHORD_KEY_TIMEOUT = 0;
 
     /**
      * Defines the duration in milliseconds a user needs to hold down the
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6e17ac9..ccce565 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -53,6 +53,9 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
@@ -462,6 +465,7 @@
     // used in relayout to get SurfaceControl size
     // for BLAST adapter surface setup
     private final Point mSurfaceSize = new Point();
+    private final Point mLastSurfaceSize = new Point();
 
     final Rect mTempRect; // used in the transaction to not thrash the heap.
     final Rect mVisRect; // used to retrieve visible rect of focused view.
@@ -1439,14 +1443,13 @@
             }
 
             // Don't lose the mode we last auto-computed.
-            if ((attrs.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
+            if ((attrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
                     == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
                 mWindowAttributes.softInputMode = (mWindowAttributes.softInputMode
-                        & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
-                        | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST);
+                        & ~SOFT_INPUT_MASK_ADJUST) | (oldSoftInputMode & SOFT_INPUT_MASK_ADJUST);
             }
 
-            if ((changes & LayoutParams.SOFT_INPUT_MODE_CHANGED) != 0) {
+            if (mWindowAttributes.softInputMode != oldSoftInputMode) {
                 requestFitSystemWindows();
             }
 
@@ -2063,6 +2066,7 @@
         final int sysUiVis = inOutParams.systemUiVisibility | inOutParams.subtreeSystemUiVisibility;
         final int flags = inOutParams.flags;
         final int type = inOutParams.type;
+        final int adjust = inOutParams.softInputMode & SOFT_INPUT_MASK_ADJUST;
 
         if ((inOutParams.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) {
             inOutParams.insetsFlags.appearance = 0;
@@ -2088,12 +2092,13 @@
             }
         }
 
+        inOutParams.privateFlags &= ~PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+
         if ((inOutParams.privateFlags & PRIVATE_FLAG_FIT_INSETS_CONTROLLED) != 0) {
             return;
         }
 
         int types = inOutParams.getFitInsetsTypes();
-        int sides = inOutParams.getFitInsetsSides();
         boolean ignoreVis = inOutParams.isFitInsetsIgnoringVisibility();
 
         if (((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0
@@ -2108,10 +2113,13 @@
         if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
             ignoreVis = true;
         } else if ((types & Type.systemBars()) == Type.systemBars()) {
-            types |= Type.ime();
+            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
+                types |= Type.ime();
+            } else {
+                inOutParams.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+            }
         }
         inOutParams.setFitInsetsTypes(types);
-        inOutParams.setFitInsetsSides(sides);
         inOutParams.setFitInsetsIgnoringVisibility(ignoreVis);
 
         // The fitting of insets are not really controlled by the clients, so we remove the flag.
@@ -2246,9 +2254,11 @@
 
     /* package */ WindowInsets getWindowInsets(boolean forceConstruct) {
         if (mLastWindowInsets == null || forceConstruct) {
+            final Configuration config = mContext.getResources().getConfiguration();
             mLastWindowInsets = mInsetsController.calculateInsets(
-                    mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(),
+                    config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
+                    mPendingDisplayCutout.get(), mWindowAttributes.type,
+                    config.windowConfiguration.getWindowingMode(),
                     mWindowAttributes.softInputMode, mWindowAttributes.flags,
                     (mWindowAttributes.systemUiVisibility
                             | mWindowAttributes.subtreeSystemUiVisibility));
@@ -2481,8 +2491,7 @@
 
         if (mFirst || mAttachInfo.mViewVisibilityChanged) {
             mAttachInfo.mViewVisibilityChanged = false;
-            int resizeMode = mSoftInputMode &
-                    WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+            int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST;
             // If we are in auto resize mode, then we need to determine
             // what mode to use now.
             if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
@@ -2495,11 +2504,8 @@
                 if (resizeMode == 0) {
                     resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
                 }
-                if ((lp.softInputMode &
-                        WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
-                    lp.softInputMode = (lp.softInputMode &
-                            ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
-                            resizeMode;
+                if ((lp.softInputMode & SOFT_INPUT_MASK_ADJUST) != resizeMode) {
+                    lp.softInputMode = (lp.softInputMode & ~SOFT_INPUT_MASK_ADJUST) | resizeMode;
                     params = lp;
                 }
             }
@@ -2636,9 +2642,12 @@
                 }
 
                 cutoutChanged = !mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout);
-                surfaceSizeChanged = (relayoutResult
-                        & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
-                final boolean alwaysConsumeSystemBarsChanged =
+                surfaceSizeChanged = false;
+                if (!mLastSurfaceSize.equals(mSurfaceSize)) {
+                    surfaceSizeChanged = true;
+                    mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
+                }
+                  final boolean alwaysConsumeSystemBarsChanged =
                         mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
                 updateColorModeIfNeeded(lp.getColorMode());
                 surfaceCreated = !hadSurface && mSurface.isValid();
@@ -3234,8 +3243,8 @@
 
             // Note: must be done after the focus change callbacks,
             // so all of the view state is set up correctly.
-            mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus,
-                    mWindowAttributes);
+            mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null,
+                    hasWindowFocus, mWindowAttributes);
 
             if (hasWindowFocus) {
                 // Clear the forward bit.  We can just do this directly, since
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 1a90035..e879bb4 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -32,8 +32,6 @@
 
 /**
  * Interface to control windows that generate insets.
- *
- * TODO(118118435): Needs more information and examples once the API is more baked.
  */
 public interface WindowInsetsController {
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 32ee290..0d62da6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2105,6 +2105,12 @@
         public static final int PRIVATE_FLAG_TRUSTED_OVERLAY = 0x20000000;
 
         /**
+         * Flag to indicate that the parent frame of a window should be inset by IME.
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME = 0x40000000;
+
+        /**
          * An internal annotation for flags that can be specified to {@link #softInputMode}.
          *
          * @hide
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 27fbfb6..5d53ad7 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -29,6 +29,7 @@
 import android.app.ResourcesManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -271,13 +272,14 @@
             final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
                     .getWindowInsets(attrs, mContext.getDisplayId(), systemWindowInsets,
                     stableInsets, displayCutout, insetsState);
-            final boolean isScreenRound =
-                    mContext.getResources().getConfiguration().isScreenRound();
+            final Configuration config = mContext.getResources().getConfiguration();
+            final boolean isScreenRound = config.isScreenRound();
+            final int windowingMode = config.windowConfiguration.getWindowingMode();
             if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
                 return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
                         isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
-                        SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
-                        SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */);
+                        SOFT_INPUT_ADJUST_NOTHING, attrs.flags, SYSTEM_UI_FLAG_VISIBLE, attrs.type,
+                        windowingMode, null /* typeSideMap */);
             } else {
                 return new WindowInsets.Builder()
                         .setAlwaysConsumeSystemBars(alwaysConsumeSystemBars)
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index eb67191..ae853e9 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -18,6 +18,7 @@
 
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.RemoteCallback;
 import android.view.accessibility.IWindowMagnificationConnectionCallback;
 
 /**
@@ -37,8 +38,10 @@
      *                or {@link Float#NaN} to leave unchanged.
      * @param centerY the screen-relative Y coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
+     * @param endCallback The callback called when the animation is completed.
      */
-    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
+    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+        in RemoteCallback endCallback);
 
     /**
      * Sets the scale of the window magnifier on specified display.
@@ -52,8 +55,9 @@
      * Disables window magnification on specified display with animation.
      *
      * @param displayId The logical display id.
+     * @param endCallback The callback called when the animation is completed.
      */
-    void disableWindowMagnification(int displayId);
+    void disableWindowMagnification(int displayId, in RemoteCallback endCallback);
 
     /**
      * Moves the window magnifier on the specified display. It has no effect while animating.
diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
new file mode 100644
index 0000000..491f7fb
--- /dev/null
+++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+/**
+ * A callback for magnification animation result.
+ * @hide
+ */
+public interface MagnificationAnimationCallback {
+    /**
+     * Called when the animation is finished or interrupted during animating.
+     *
+     * @param success {@code true} if animating successfully with given spec or the spec did not
+     *                change. Otherwise {@code false}
+     */
+    void onResult(boolean success);
+}
diff --git a/core/java/android/view/inputmethod/TEST_MAPPING b/core/java/android/view/inputmethod/TEST_MAPPING
new file mode 100644
index 0000000..4b2ea1a
--- /dev/null
+++ b/core/java/android/view/inputmethod/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsAutoFillServiceTestCases",
+      "options": [
+        {
+          "include-filter": "android.autofillservice.cts.inline"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.AppModeFull"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 35605c4..796654a4d 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -54,6 +54,7 @@
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
@@ -314,9 +315,6 @@
         setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
 
         setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));
-        // onProgressRefresh() is only called when the progress changes. So we should set
-        // stateDescription during initialization here.
-        super.setStateDescription(formatStateDescription(mProgress));
 
         setSecondaryProgress(a.getInt(
                 R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
@@ -1627,7 +1625,8 @@
     }
 
     void onProgressRefresh(float scale, boolean fromUser, int progress) {
-        if (mCustomStateDescription == null) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled()
+                && mCustomStateDescription == null) {
             super.setStateDescription(formatStateDescription(mProgress));
         }
     }
@@ -2351,6 +2350,7 @@
                     AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(),
                     getProgress());
             info.setRangeInfo(rangeInfo);
+            info.setStateDescription(formatStateDescription(mProgress));
         }
     }
 
diff --git a/core/java/android/widget/TEST_MAPPING b/core/java/android/widget/TEST_MAPPING
index df3024e..b5beac9 100644
--- a/core/java/android/widget/TEST_MAPPING
+++ b/core/java/android/widget/TEST_MAPPING
@@ -36,17 +36,6 @@
       "name": "CtsAutoFillServiceTestCases",
       "options": [
         {
-          "include-filter": "android.autofillservice.cts.AutofillValueTest"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
-    },
-    {
-      "name": "CtsAutoFillServiceTestCases",
-      "options": [
-        {
           "include-filter": "android.autofillservice.cts.CheckoutActivityTest"
         },
         {
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index f035d36..78fa303 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -20,7 +20,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
 import android.os.RemoteException;
-import android.util.Singleton;
 import android.view.SurfaceControl;
 
 /**
@@ -135,22 +134,12 @@
         }
     };
 
-    private static IDisplayAreaOrganizerController getController() {
-        return IDisplayAreaOrganizerControllerSingleton.get();
+    private IDisplayAreaOrganizerController getController() {
+        try {
+            return getWindowOrganizerController().getDisplayAreaOrganizerController();
+        } catch (RemoteException e) {
+            return null;
+        }
     }
 
-    private static final Singleton<IDisplayAreaOrganizerController>
-            IDisplayAreaOrganizerControllerSingleton =
-            new Singleton<IDisplayAreaOrganizerController>() {
-                @Override
-                protected IDisplayAreaOrganizerController create() {
-                    try {
-                        return getWindowOrganizerController()
-                                .getDisplayAreaOrganizerController();
-                    } catch (RemoteException e) {
-                        return null;
-                    }
-                }
-            };
-
 }
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 92fa80e..12b16ff 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -60,5 +60,6 @@
      * Requests that the given task organizer is notified when back is pressed on the root activity
      * of one of its controlled tasks.
      */
-    void setInterceptBackPressedOnTaskRoot(ITaskOrganizer organizer, boolean interceptBackPressed);
+    void setInterceptBackPressedOnTaskRoot(in WindowContainerToken task,
+            boolean interceptBackPressed);
 }
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 7ec4f99..d8f2bb2 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -23,9 +23,10 @@
 import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.os.RemoteException;
-import android.util.Singleton;
 import android.view.SurfaceControl;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.List;
 
 /**
@@ -35,13 +36,25 @@
 @TestApi
 public class TaskOrganizer extends WindowOrganizer {
 
+    private ITaskOrganizerController mTaskOrganizerController;
+
+    public TaskOrganizer() {
+        mTaskOrganizerController = getController();
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public TaskOrganizer(ITaskOrganizerController taskOrganizerController) {
+        mTaskOrganizerController = taskOrganizerController;
+    }
+
     /**
      * Register a TaskOrganizer to manage tasks as they enter a supported windowing mode.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     public final void registerOrganizer() {
         try {
-            getController().registerTaskOrganizer(mInterface);
+            mTaskOrganizerController.registerTaskOrganizer(mInterface);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -51,7 +64,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     public final void unregisterOrganizer() {
         try {
-            getController().unregisterTaskOrganizer(mInterface);
+            mTaskOrganizerController.unregisterTaskOrganizer(mInterface);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -78,9 +91,9 @@
     /** Creates a persistent root task in WM for a particular windowing-mode. */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     @Nullable
-    public static ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode) {
+    public ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode) {
         try {
-            return getController().createRootTask(displayId, windowingMode);
+            return mTaskOrganizerController.createRootTask(displayId, windowingMode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -88,9 +101,9 @@
 
     /** Deletes a persistent root task in WM */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public static boolean deleteRootTask(@NonNull WindowContainerToken task) {
+    public boolean deleteRootTask(@NonNull WindowContainerToken task) {
         try {
-            return getController().deleteRootTask(task);
+            return mTaskOrganizerController.deleteRootTask(task);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -99,10 +112,10 @@
     /** Gets direct child tasks (ordered from top-to-bottom) */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     @Nullable
-    public static List<ActivityManager.RunningTaskInfo> getChildTasks(
+    public List<ActivityManager.RunningTaskInfo> getChildTasks(
             @NonNull WindowContainerToken parent, @NonNull int[] activityTypes) {
         try {
-            return getController().getChildTasks(parent, activityTypes);
+            return mTaskOrganizerController.getChildTasks(parent, activityTypes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -111,10 +124,10 @@
     /** Gets all root tasks on a display (ordered from top-to-bottom) */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     @Nullable
-    public static List<ActivityManager.RunningTaskInfo> getRootTasks(
+    public List<ActivityManager.RunningTaskInfo> getRootTasks(
             int displayId, @NonNull int[] activityTypes) {
         try {
-            return getController().getRootTasks(displayId, activityTypes);
+            return mTaskOrganizerController.getRootTasks(displayId, activityTypes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -123,9 +136,9 @@
     /** Get the root task which contains the current ime target */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     @Nullable
-    public static WindowContainerToken getImeTarget(int display) {
+    public WindowContainerToken getImeTarget(int display) {
         try {
-            return getController().getImeTarget(display);
+            return mTaskOrganizerController.getImeTarget(display);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -136,9 +149,9 @@
      * root and thus new tasks just end up directly on the display.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public static void setLaunchRoot(int displayId, @NonNull WindowContainerToken root) {
+    public void setLaunchRoot(int displayId, @NonNull WindowContainerToken root) {
         try {
-            getController().setLaunchRoot(displayId, root);
+            mTaskOrganizerController.setLaunchRoot(displayId, root);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -149,9 +162,10 @@
      * of one of its controlled tasks.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void setInterceptBackPressedOnTaskRoot(boolean interceptBackPressed) {
+    public void setInterceptBackPressedOnTaskRoot(@NonNull WindowContainerToken task,
+            boolean interceptBackPressed) {
         try {
-            getController().setInterceptBackPressedOnTaskRoot(mInterface, interceptBackPressed);
+            mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(task, interceptBackPressed);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -180,19 +194,11 @@
         }
     };
 
-    private static ITaskOrganizerController getController() {
-        return ITaskOrganizerControllerSingleton.get();
+    private ITaskOrganizerController getController() {
+        try {
+            return getWindowOrganizerController().getTaskOrganizerController();
+        } catch (RemoteException e) {
+            return null;
+        }
     }
-
-    private static final Singleton<ITaskOrganizerController> ITaskOrganizerControllerSingleton =
-            new Singleton<ITaskOrganizerController>() {
-                @Override
-                protected ITaskOrganizerController create() {
-                    try {
-                        return getWindowOrganizerController().getTaskOrganizerController();
-                    } catch (RemoteException e) {
-                        return null;
-                    }
-                }
-            };
 }
diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java
index 46c72f8..1e293df 100644
--- a/core/java/android/window/TaskOrganizerTaskEmbedder.java
+++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java
@@ -74,7 +74,7 @@
         // windowing mode tasks. Plan is to migrate this to a wm-shell front-end when that
         // infrastructure is ready.
         // mTaskOrganizer.registerOrganizer();
-        mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true);
+        // mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true);
 
         return super.onInitialize();
     }
@@ -109,7 +109,7 @@
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setHidden(mTaskToken, false /* hidden */);
-        WindowOrganizer.applyTransaction(wct);
+        mTaskOrganizer.applyTransaction(wct);
         // TODO(b/151449487): Only call callback once we enable synchronization
         if (mListener != null) {
             mListener.onTaskVisibilityChanged(getTaskId(), true);
@@ -133,7 +133,7 @@
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setHidden(mTaskToken, true /* hidden */);
-        WindowOrganizer.applyTransaction(wct);
+        mTaskOrganizer.applyTransaction(wct);
         // TODO(b/151449487): Only call callback once we enable synchronization
         if (mListener != null) {
             mListener.onTaskVisibilityChanged(getTaskId(), false);
@@ -165,7 +165,7 @@
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setBounds(mTaskToken, screenBounds);
         // TODO(b/151449487): Enable synchronization
-        WindowOrganizer.applyTransaction(wct);
+        mTaskOrganizer.applyTransaction(wct);
     }
 
     /**
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index ff40dda..97a97d9 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -38,7 +38,7 @@
      * @param t The transaction to apply.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public static void applyTransaction(@NonNull WindowContainerTransaction t) {
+    public void applyTransaction(@NonNull WindowContainerTransaction t) {
         try {
             getWindowOrganizerController().applyTransaction(t);
         } catch (RemoteException e) {
@@ -74,7 +74,7 @@
      */
     @Nullable
     @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
-    public static SurfaceControl takeScreenshot(@NonNull WindowContainerToken token) {
+    public SurfaceControl takeScreenshot(@NonNull WindowContainerToken token) {
         try {
             SurfaceControl surfaceControl = new SurfaceControl();
             if (getWindowOrganizerController().takeScreenshot(token, surfaceControl)) {
@@ -88,7 +88,7 @@
     }
 
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    static IWindowOrganizerController getWindowOrganizerController() {
+    IWindowOrganizerController getWindowOrganizerController() {
         return IWindowOrganizerControllerSingleton.get();
     }
 
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index eb59f0f..8b4fddb 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -390,9 +390,9 @@
     public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled";
 
     /**
-     * (boolean) Whether to enable user-drag resizing for PIP.
+     * (boolean) Whether to enable pinch resizing for PIP.
      */
-    public static final String PIP_USER_RESIZE = "pip_user_resize";
+    public static final String PIP_PINCH_RESIZE = "pip_pinch_resize";
 
     /**
      * (float) Bottom height in DP for Back Gesture.
@@ -409,6 +409,11 @@
      */
     public static final String BACK_GESTURE_SLOP_MULTIPLIER = "back_gesture_slop_multiplier";
 
+    /**
+     * (long) Screenshot keychord delay (how long the buttons must be pressed), in ms
+     */
+    public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index a50a522..3b5fecf 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -113,6 +113,14 @@
         // Default is no-op
     }
 
+    /**
+     * Callback indicating that the given document has been deleted or moved. This gives
+     * the provider a hook to revoke the uri permissions.
+     */
+    protected void onDocIdDeleted(String docId) {
+        // Default is no-op
+    }
+
     @Override
     public boolean onCreate() {
         throw new UnsupportedOperationException(
@@ -283,6 +291,7 @@
 
         final String afterDocId = getDocIdForFile(after);
         onDocIdChanged(docId);
+        onDocIdDeleted(docId);
         onDocIdChanged(afterDocId);
 
         final File afterVisibleFile = getFileForDocId(afterDocId, true);
@@ -312,6 +321,7 @@
 
         final String docId = getDocIdForFile(after);
         onDocIdChanged(sourceDocumentId);
+        onDocIdDeleted(sourceDocumentId);
         onDocIdChanged(docId);
         moveInMediaStore(visibleFileBefore, getFileForDocId(docId, true));
 
@@ -343,6 +353,7 @@
         }
 
         onDocIdChanged(docId);
+        onDocIdDeleted(docId);
         removeFromMediaStore(visibleFile);
     }
 
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
new file mode 100644
index 0000000..e5c8450
--- /dev/null
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.jank;
+
+import android.annotation.NonNull;
+import android.graphics.HardwareRendererObserver;
+import android.os.Handler;
+import android.os.Trace;
+import android.util.Log;
+import android.view.FrameMetrics;
+import android.view.ThreadedRenderer;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor.Session;
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * @hide
+ */
+public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
+    private static final String TAG = FrameTracker.class.getSimpleName();
+    private static final boolean DEBUG = false;
+    //TODO (163431584): need also consider other refresh rates.
+    private static final long JANK_THRESHOLD_NANOS = 1000000000 / 60;
+    private static final long UNKNOWN_TIMESTAMP = -1;
+
+    private final HardwareRendererObserver mObserver;
+    private final ThreadedRendererWrapper mRendererWrapper;
+    private final FrameMetricsWrapper mMetricsWrapper;
+
+    private long mBeginTime = UNKNOWN_TIMESTAMP;
+    private long mEndTime = UNKNOWN_TIMESTAMP;
+    private boolean mShouldTriggerTrace;
+    private long mTotalFramesCount = 0;
+    private long mMissedFramesCount = 0;
+    private long mMaxFrameTimeNanos = 0;
+
+    private Session mSession;
+
+    public FrameTracker(@NonNull Session session,
+            @NonNull Handler handler, @NonNull ThreadedRenderer renderer) {
+        mSession = session;
+        mRendererWrapper = new ThreadedRendererWrapper(renderer);
+        mMetricsWrapper = new FrameMetricsWrapper();
+        mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
+    }
+
+    /**
+     * This constructor is only for unit tests.
+     * @param session a trace session.
+     * @param renderer a test double for ThreadedRenderer
+     * @param metrics a test double for FrameMetrics
+     */
+    @VisibleForTesting
+    public FrameTracker(@NonNull Session session, Handler handler,
+            @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) {
+        mSession = session;
+        mRendererWrapper = renderer;
+        mMetricsWrapper = metrics;
+        mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
+    }
+
+    /**
+     * Begin a trace session of the CUJ.
+     */
+    public void begin() {
+        long timestamp = System.nanoTime();
+        if (DEBUG) {
+            Log.d(TAG, "begin: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime
+                    + ", end(ns)=" + mEndTime + ", session=" + mSession);
+        }
+        if (mBeginTime != UNKNOWN_TIMESTAMP && mEndTime == UNKNOWN_TIMESTAMP) {
+            // We have an ongoing tracing already, skip subsequent calls.
+            return;
+        }
+        mBeginTime = timestamp;
+        mEndTime = UNKNOWN_TIMESTAMP;
+        Trace.beginAsyncSection(mSession.getName(), (int) mBeginTime);
+        mRendererWrapper.addObserver(mObserver);
+    }
+
+    /**
+     * End the trace session of the CUJ.
+     */
+    public void end() {
+        long timestamp = System.nanoTime();
+        if (DEBUG) {
+            Log.d(TAG, "end: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime
+                    + ", end(ns)=" + mEndTime + ", session=" + mSession);
+        }
+        if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) {
+            // We haven't started a trace yet.
+            return;
+        }
+        mEndTime = timestamp;
+        Trace.endAsyncSection(mSession.getName(), (int) mBeginTime);
+    }
+
+    @Override
+    public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
+        // Since this callback might come a little bit late after the end() call.
+        // We should keep tracking the begin / end timestamp.
+        // Then compare with vsync timestamp to check if the frame is in the duration of the CUJ.
+
+        if (mBeginTime == UNKNOWN_TIMESTAMP) return; // We haven't started tracing yet.
+        long vsyncTimestamp = mMetricsWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP);
+        if (vsyncTimestamp < mBeginTime) return; // The tracing has been started.
+
+        // If the end time has not been set, we are still in the tracing.
+        if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime) {
+            // The tracing has been ended, remove the observer, see if need to trigger perfetto.
+            mRendererWrapper.removeObserver(mObserver);
+            // Trigger perfetto if necessary.
+            if (mShouldTriggerTrace) {
+                if (DEBUG) {
+                    Log.v(TAG, "Found janky frame, triggering perfetto.");
+                }
+                triggerPerfetto();
+            }
+            if (mSession.logToStatsd()) {
+                FrameworkStatsLog.write(
+                        FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
+                        mSession.getStatsdInteractionType(),
+                        mTotalFramesCount,
+                        mMissedFramesCount,
+                        mMaxFrameTimeNanos);
+            }
+            return;
+        }
+
+        long totalDurationNanos = mMetricsWrapper.getMetric(FrameMetrics.TOTAL_DURATION);
+        boolean isFirstFrame = mMetricsWrapper.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1;
+        boolean isJankyFrame = !isFirstFrame && totalDurationNanos > JANK_THRESHOLD_NANOS;
+
+        mTotalFramesCount += 1;
+
+        if (!isFirstFrame) {
+            mMaxFrameTimeNanos = Math.max(totalDurationNanos, mMaxFrameTimeNanos);
+        }
+
+        if (isJankyFrame) {
+            mMissedFramesCount += 1;
+            mShouldTriggerTrace = true;
+        }
+    }
+
+    /**
+     * Trigger the prefetto daemon.
+     */
+    @VisibleForTesting
+    public void triggerPerfetto() {
+        InteractionJankMonitor.trigger();
+    }
+
+    /**
+     * A wrapper class that we can spy FrameMetrics (a final class) in unit tests.
+     */
+    public static class FrameMetricsWrapper {
+        private FrameMetrics mFrameMetrics;
+
+        public FrameMetricsWrapper() {
+            mFrameMetrics = new FrameMetrics();
+        }
+
+        /**
+         * Wrapper method.
+         * @return timing data of the metrics
+         */
+        public long[] getTiming() {
+            return mFrameMetrics.mTimingData;
+        }
+
+        /**
+         * Wrapper method.
+         * @param index specific index of the timing data
+         * @return the timing data of the specified index
+         */
+        public long getMetric(int index) {
+            return mFrameMetrics.getMetric(index);
+        }
+    }
+
+    /**
+     * A wrapper class that we can spy ThreadedRenderer (a final class) in unit tests.
+     */
+    public static class ThreadedRendererWrapper {
+        private ThreadedRenderer mRenderer;
+
+        public ThreadedRendererWrapper(ThreadedRenderer renderer) {
+            mRenderer = renderer;
+        }
+
+        /**
+         * Wrapper method.
+         * @param observer observer
+         */
+        public void addObserver(HardwareRendererObserver observer) {
+            mRenderer.addObserver(observer);
+        }
+
+        /**
+         * Wrapper method.
+         * @param observer observer
+         */
+        public void removeObserver(HardwareRendererObserver observer) {
+            mRenderer.removeObserver(observer);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
new file mode 100644
index 0000000..5a0cbf9
--- /dev/null
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.jank;
+
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.HandlerThread;
+import android.view.ThreadedRenderer;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class let users to begin and end the always on tracing mechanism.
+ * @hide
+ */
+public class InteractionJankMonitor {
+    private static final String TAG = InteractionJankMonitor.class.getSimpleName();
+    private static final boolean DEBUG = false;
+    private static final Object LOCK = new Object();
+
+    // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
+    public static final int CUJ_NOTIFICATION_SHADE_MOTION = 0;
+    public static final int CUJ_NOTIFICATION_SHADE_GESTURE = 1;
+
+    private static final int NO_STATSD_LOGGING = -1;
+
+    // Used to convert CujType to InteractionType enum value for statsd logging.
+    // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
+    private static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
+            NO_STATSD_LOGGING,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
+    };
+
+    private static ThreadedRenderer sRenderer;
+    private static Map<String, FrameTracker> sRunningTracker;
+    private static HandlerThread sWorker;
+    private static boolean sInitialized;
+
+    /** @hide */
+    @IntDef({
+            CUJ_NOTIFICATION_SHADE_MOTION,
+            CUJ_NOTIFICATION_SHADE_GESTURE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CujType {}
+
+    /**
+     * @param view Any view in the view tree to get context and ThreadedRenderer.
+     */
+    public static void init(@NonNull View view) {
+        init(view, null, null, null);
+    }
+
+    /**
+     * Should be only invoked internally or from unit tests.
+     */
+    @VisibleForTesting
+    public static void init(@NonNull View view, @NonNull ThreadedRenderer renderer,
+            @NonNull Map<String, FrameTracker> map, @NonNull HandlerThread worker) {
+        //TODO (163505250): This should be no-op if not in droid food rom.
+        synchronized (LOCK) {
+            if (!sInitialized) {
+                if (!view.isAttachedToWindow()) {
+                    throw new IllegalStateException("View is not attached!");
+                }
+                sRenderer = renderer == null ? view.getThreadedRenderer() : renderer;
+                sRunningTracker = map == null ? new HashMap<>() : map;
+                sWorker = worker == null ? new HandlerThread("Aot-Worker") : worker;
+                sWorker.start();
+                sInitialized = true;
+            }
+        }
+    }
+
+    /**
+     * Must invoke init() before invoking this method.
+     */
+    public static void begin(@NonNull @CujType int cujType) {
+        begin(cujType, null);
+    }
+
+    /**
+     * Should be only invoked internally or from unit tests.
+     */
+    @VisibleForTesting
+    public static void begin(@NonNull @CujType int cujType, FrameTracker tracker) {
+        //TODO (163505250): This should be no-op if not in droid food rom.
+        //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads.
+        synchronized (LOCK) {
+            checkInitStateLocked();
+            Session session = new Session(cujType);
+            FrameTracker currentTracker = getTracker(session.getName());
+            if (currentTracker != null) return;
+            if (tracker == null) {
+                tracker = new FrameTracker(session, sWorker.getThreadHandler(), sRenderer);
+            }
+            sRunningTracker.put(session.getName(), tracker);
+            tracker.begin();
+        }
+    }
+
+    /**
+     * Must invoke init() before invoking this method.
+     */
+    public static void end(@NonNull @CujType int cujType) {
+        //TODO (163505250): This should be no-op if not in droid food rom.
+        //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads.
+        synchronized (LOCK) {
+            checkInitStateLocked();
+            Session session = new Session(cujType);
+            FrameTracker tracker = getTracker(session.getName());
+            if (tracker != null) {
+                tracker.end();
+                sRunningTracker.remove(session.getName());
+            }
+        }
+    }
+
+    private static void checkInitStateLocked() {
+        if (!sInitialized) {
+            throw new IllegalStateException("InteractionJankMonitor not initialized!");
+        }
+    }
+
+    /**
+     * Should be only invoked from unit tests.
+     */
+    @VisibleForTesting
+    public static void reset() {
+        sInitialized = false;
+        sRenderer = null;
+        sRunningTracker = null;
+        if (sWorker != null) {
+            sWorker.quit();
+            sWorker = null;
+        }
+    }
+
+    private static FrameTracker getTracker(String sessionName) {
+        synchronized (LOCK) {
+            return sRunningTracker.get(sessionName);
+        }
+    }
+
+    /**
+     * Trigger the perfetto daemon to collect and upload data.
+     */
+    public static void trigger() {
+        sWorker.getThreadHandler().post(
+                () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK));
+    }
+
+    /**
+     * A class to represent a session.
+     */
+    public static class Session {
+        private @CujType int mId;
+
+        public Session(@CujType int session) {
+            mId = session;
+        }
+
+        public int getId() {
+            return mId;
+        }
+
+        public int getStatsdInteractionType() {
+            return CUJ_TO_STATSD_INTERACTION_TYPE[mId];
+        }
+
+        /** Describes whether the measurement from this session should be written to statsd. */
+        public boolean logToStatsd() {
+            return getStatsdInteractionType() != NO_STATSD_LOGGING;
+        }
+
+        public String getName() {
+            return "CujType<" + mId + ">";
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/jank/PerfettoTrigger.java b/core/java/com/android/internal/jank/PerfettoTrigger.java
new file mode 100644
index 0000000..6c8d3cd
--- /dev/null
+++ b/core/java/com/android/internal/jank/PerfettoTrigger.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+//TODO (165884885): Make PerfettoTrigger more generic and move it to another package.
+package com.android.internal.jank;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A trigger implementation with perfetto backend.
+ * @hide
+ */
+public class PerfettoTrigger {
+    private static final String TAG = PerfettoTrigger.class.getSimpleName();
+    private static final boolean DEBUG = false;
+    private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
+    private static final String[] TRIGGER_TYPE_NAMES = new String[] { "jank-tracker" };
+    public static final int TRIGGER_TYPE_JANK = 0;
+
+    /** @hide */
+    @IntDef({
+            TRIGGER_TYPE_JANK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TriggerType {}
+
+    /**
+     * @param type the trigger type
+     */
+    public static void trigger(@NonNull @TriggerType int type) {
+        try {
+            Token token = new Token(type, TRIGGER_TYPE_NAMES[type]);
+            ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, token.getName());
+            if (DEBUG) {
+                StringBuilder sb = new StringBuilder();
+                for (String arg : pb.command()) {
+                    sb.append(arg).append(" ");
+                }
+                Log.d(TAG, "Triggering " + sb.toString());
+            }
+            Process process = pb.start();
+            if (DEBUG) {
+                readConsoleOutput(process);
+            }
+        } catch (IOException | InterruptedException e) {
+            Log.w(TAG, "Failed to trigger " + type, e);
+        }
+    }
+
+    private static void readConsoleOutput(@NonNull Process process)
+            throws IOException, InterruptedException {
+        process.waitFor();
+        try (BufferedReader errReader =
+                     new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
+            StringBuilder errLine = new StringBuilder();
+            String line;
+            while ((line = errReader.readLine()) != null) {
+                errLine.append(line).append("\n");
+            }
+            errLine.append(", code=").append(process.exitValue());
+            Log.d(TAG, "err message=" + errLine.toString());
+        }
+    }
+
+    /**
+     * Token which is used to trigger perfetto.
+     */
+    public static class Token {
+        private int mType;
+        private String mName;
+
+        Token(@TriggerType int type, String name) {
+            mType = type;
+            mName = name;
+        }
+
+        /**
+         * Get trigger type.
+         * @return trigger type, should be @TriggerType
+         */
+        public int getType() {
+            return mType;
+        }
+
+        /**
+         * Get name of this token as the argument while triggering perfetto.
+         * @return name
+         */
+        public String getName() {
+            return mName;
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 51b4119..5e34c15 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -20,8 +20,10 @@
 import android.content.ContentCaptureOptions;
 import android.content.Context;
 import android.content.res.AssetManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.view.ContextThemeWrapper;
+import android.view.Display;
 import android.view.contentcapture.ContentCaptureManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -47,9 +49,17 @@
     public DecorContext(Context baseContext, PhoneWindow phoneWindow) {
         super(null /* base */, null);
         setPhoneWindow(phoneWindow);
-        final Context displayContext = baseContext.createDisplayContext(
-                // TODO(b/149790106): Non-activity context can be passed.
-                phoneWindow.getContext().getDisplayNoVerify());
+        // TODO(b/149790106): Non-activity context can be passed.
+        final Display display = phoneWindow.getContext().getDisplayNoVerify();
+        final Context displayContext;
+        if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            // TODO(b/166174272): Creating a display context for the default display will result
+            // in additional resource creation.
+            displayContext = baseContext.createConfigurationContext(Configuration.EMPTY);
+            displayContext.updateDisplay(Display.DEFAULT_DISPLAY);
+        } else {
+            displayContext = baseContext.createDisplayContext(display);
+        }
         attachBaseContext(displayContext);
     }
 
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index b12c5e9..a6c3dd1 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -24,6 +24,7 @@
 import static android.os.Build.VERSION_CODES.N;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.clearCompatInsets;
 import static android.view.View.MeasureSpec.AT_MOST;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.getMode;
@@ -1097,10 +1098,12 @@
                 final Insets systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars());
                 final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
                         WindowInsets.Type.systemBars());
-                mLastTopInset = systemBarInsets.top;
-                mLastBottomInset = systemBarInsets.bottom;
-                mLastRightInset = systemBarInsets.right;
-                mLastLeftInset = systemBarInsets.left;
+                final boolean clearCompatInsets = clearCompatInsets(attrs.type, attrs.flags,
+                        getResources().getConfiguration().windowConfiguration.getWindowingMode());
+                mLastTopInset = clearCompatInsets ? 0 : systemBarInsets.top;
+                mLastBottomInset = clearCompatInsets ? 0 : systemBarInsets.bottom;
+                mLastRightInset = clearCompatInsets ? 0 : systemBarInsets.right;
+                mLastLeftInset = clearCompatInsets ? 0 : systemBarInsets.left;
 
                 // Don't animate if the presence of stable insets has changed, because that
                 // indicates that the window was either just added and received them for the
diff --git a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
index 1ec0206..64e0b91 100644
--- a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
+++ b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
@@ -51,8 +51,6 @@
     public static final int CTRL_BOTTOM = 0x8;
 
     // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
-    // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
-    // aspect he desires.
     @VisibleForTesting
     public static final float MIN_ASPECT = 1.2f;
 
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
new file mode 100644
index 0000000..8a4eb4a
--- /dev/null
+++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import static com.android.internal.protolog.ProtoLogFileProto.LOG;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
+import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
+import static com.android.internal.protolog.ProtoLogFileProto.VERSION;
+import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
+import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH;
+import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
+
+import android.annotation.Nullable;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogDataType;
+import com.android.internal.util.TraceBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.IllegalFormatConversionException;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+
+/**
+ * A service for the ProtoLog logging system.
+ */
+public class BaseProtoLogImpl {
+    protected static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
+
+    /**
+     * A runnable to update the cached output of {@link #isEnabled}.
+     *
+     * Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
+     * starting / stopping proto log, or enabling / disabling log groups.
+     */
+    public static Runnable sCacheUpdater = () -> { };
+
+    protected static void addLogGroupEnum(IProtoLogGroup[] config) {
+        for (IProtoLogGroup group : config) {
+            LOG_GROUPS.put(group.name(), group);
+        }
+    }
+
+    private static final String TAG = "ProtoLog";
+    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+    static final String PROTOLOG_VERSION = "1.0.0";
+
+    private final File mLogFile;
+    private final String mViewerConfigFilename;
+    private final TraceBuffer mBuffer;
+    protected final ProtoLogViewerConfigReader mViewerConfig;
+
+    private boolean mProtoLogEnabled;
+    private boolean mProtoLogEnabledLockFree;
+    private final Object mProtoLogEnabledLock = new Object();
+
+    @VisibleForTesting
+    public enum LogLevel {
+        DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
+    }
+
+    /**
+     * Main log method, do not call directly.
+     */
+    @VisibleForTesting
+    public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString, Object[] args) {
+        if (group.isLogToProto()) {
+            logToProto(messageHash, paramsMask, args);
+        }
+        if (group.isLogToLogcat()) {
+            logToLogcat(group.getTag(), level, messageHash, messageString, args);
+        }
+    }
+
+    private void logToLogcat(String tag, LogLevel level, int messageHash,
+            @Nullable String messageString, Object[] args) {
+        String message = null;
+        if (messageString == null) {
+            messageString = mViewerConfig.getViewerString(messageHash);
+        }
+        if (messageString != null) {
+            try {
+                message = String.format(messageString, args);
+            } catch (IllegalFormatConversionException ex) {
+                Slog.w(TAG, "Invalid ProtoLog format string.", ex);
+            }
+        }
+        if (message == null) {
+            StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
+            for (Object o : args) {
+                builder.append(" ").append(o);
+            }
+            message = builder.toString();
+        }
+        passToLogcat(tag, level, message);
+    }
+
+    /**
+     * SLog wrapper.
+     */
+    @VisibleForTesting
+    public void passToLogcat(String tag, LogLevel level, String message) {
+        switch (level) {
+            case DEBUG:
+                Slog.d(tag, message);
+                break;
+            case VERBOSE:
+                Slog.v(tag, message);
+                break;
+            case INFO:
+                Slog.i(tag, message);
+                break;
+            case WARN:
+                Slog.w(tag, message);
+                break;
+            case ERROR:
+                Slog.e(tag, message);
+                break;
+            case WTF:
+                Slog.wtf(tag, message);
+                break;
+        }
+    }
+
+    private void logToProto(int messageHash, int paramsMask, Object[] args) {
+        if (!isProtoEnabled()) {
+            return;
+        }
+        try {
+            ProtoOutputStream os = new ProtoOutputStream();
+            long token = os.start(LOG);
+            os.write(MESSAGE_HASH, messageHash);
+            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+
+            if (args != null) {
+                int argIndex = 0;
+                ArrayList<Long> longParams = new ArrayList<>();
+                ArrayList<Double> doubleParams = new ArrayList<>();
+                ArrayList<Boolean> booleanParams = new ArrayList<>();
+                for (Object o : args) {
+                    int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
+                    try {
+                        switch (type) {
+                            case LogDataType.STRING:
+                                os.write(STR_PARAMS, o.toString());
+                                break;
+                            case LogDataType.LONG:
+                                longParams.add(((Number) o).longValue());
+                                break;
+                            case LogDataType.DOUBLE:
+                                doubleParams.add(((Number) o).doubleValue());
+                                break;
+                            case LogDataType.BOOLEAN:
+                                booleanParams.add((boolean) o);
+                                break;
+                        }
+                    } catch (ClassCastException ex) {
+                        // Should not happen unless there is an error in the ProtoLogTool.
+                        os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
+                        Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
+                    }
+                    argIndex++;
+                }
+                if (longParams.size() > 0) {
+                    os.writePackedSInt64(SINT64_PARAMS,
+                            longParams.stream().mapToLong(i -> i).toArray());
+                }
+                if (doubleParams.size() > 0) {
+                    os.writePackedDouble(DOUBLE_PARAMS,
+                            doubleParams.stream().mapToDouble(i -> i).toArray());
+                }
+                if (booleanParams.size() > 0) {
+                    boolean[] arr = new boolean[booleanParams.size()];
+                    for (int i = 0; i < booleanParams.size(); i++) {
+                        arr[i] = booleanParams.get(i);
+                    }
+                    os.writePackedBool(BOOLEAN_PARAMS, arr);
+                }
+            }
+            os.end(token);
+            mBuffer.add(os);
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception while logging to proto", e);
+        }
+    }
+
+    public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
+            ProtoLogViewerConfigReader viewerConfig) {
+        mLogFile = file;
+        mBuffer = new TraceBuffer(bufferCapacity);
+        mViewerConfigFilename = viewerConfigFilename;
+        mViewerConfig = viewerConfig;
+    }
+
+    /**
+     * Starts the logging a circular proto buffer.
+     *
+     * @param pw Print writer
+     */
+    public void startProtoLog(@Nullable PrintWriter pw) {
+        if (isProtoEnabled()) {
+            return;
+        }
+        synchronized (mProtoLogEnabledLock) {
+            logAndPrintln(pw, "Start logging to " + mLogFile + ".");
+            mBuffer.resetBuffer();
+            mProtoLogEnabled = true;
+            mProtoLogEnabledLockFree = true;
+        }
+        sCacheUpdater.run();
+    }
+
+    /**
+     * Stops logging to proto.
+     *
+     * @param pw          Print writer
+     * @param writeToFile If the current buffer should be written to disk or not
+     */
+    public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
+        if (!isProtoEnabled()) {
+            return;
+        }
+        synchronized (mProtoLogEnabledLock) {
+            logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
+            mProtoLogEnabled = mProtoLogEnabledLockFree = false;
+            if (writeToFile) {
+                writeProtoLogToFileLocked();
+                logAndPrintln(pw, "Log written to " + mLogFile + ".");
+            }
+            if (mProtoLogEnabled) {
+                logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
+                throw new IllegalStateException("logging enabled while waiting for flush.");
+            }
+        }
+        sCacheUpdater.run();
+    }
+
+    /**
+     * Returns {@code true} iff logging to proto is enabled.
+     */
+    public boolean isProtoEnabled() {
+        return mProtoLogEnabledLockFree;
+    }
+
+    protected int setLogging(boolean setTextLogging, boolean value, PrintWriter pw,
+            String... groups) {
+        for (int i = 0; i < groups.length; i++) {
+            String group = groups[i];
+            IProtoLogGroup g = LOG_GROUPS.get(group);
+            if (g != null) {
+                System.out.println("G: "+ g);
+                if (setTextLogging) {
+                    g.setLogToLogcat(value);
+                } else {
+                    g.setLogToProto(value);
+                }
+            } else {
+                logAndPrintln(pw, "No IProtoLogGroup named " + group);
+                return -1;
+            }
+        }
+        sCacheUpdater.run();
+        return 0;
+    }
+
+    private int unknownCommand(PrintWriter pw) {
+        pw.println("Unknown command");
+        pw.println("Window manager logging options:");
+        pw.println("  start: Start proto logging");
+        pw.println("  stop: Stop proto logging");
+        pw.println("  enable [group...]: Enable proto logging for given groups");
+        pw.println("  disable [group...]: Disable proto logging for given groups");
+        pw.println("  enable-text [group...]: Enable logcat logging for given groups");
+        pw.println("  disable-text [group...]: Disable logcat logging for given groups");
+        return -1;
+    }
+
+    /**
+     * Responds to a shell command.
+     */
+    public int onShellCommand(ShellCommand shell) {
+        PrintWriter pw = shell.getOutPrintWriter();
+        String cmd = shell.getNextArg();
+        if (cmd == null) {
+            return unknownCommand(pw);
+        }
+        ArrayList<String> args = new ArrayList<>();
+        String arg;
+        while ((arg = shell.getNextArg()) != null) {
+            args.add(arg);
+        }
+        String[] groups = args.toArray(new String[args.size()]);
+        switch (cmd) {
+            case "start":
+                startProtoLog(pw);
+                return 0;
+            case "stop":
+                stopProtoLog(pw, true);
+                return 0;
+            case "status":
+                logAndPrintln(pw, getStatus());
+                return 0;
+            case "enable":
+                return setLogging(false, true, pw, groups);
+            case "enable-text":
+                mViewerConfig.loadViewerConfig(pw, mViewerConfigFilename);
+                return setLogging(true, true, pw, groups);
+            case "disable":
+                return setLogging(false, false, pw, groups);
+            case "disable-text":
+                return setLogging(true, false, pw, groups);
+            default:
+                return unknownCommand(pw);
+        }
+    }
+
+    /**
+     * Returns a human-readable ProtoLog status text.
+     */
+    public String getStatus() {
+        return "ProtoLog status: "
+                + ((isProtoEnabled()) ? "Enabled" : "Disabled")
+                + "\nEnabled log groups: \n  Proto: "
+                + LOG_GROUPS.values().stream().filter(
+                    it -> it.isEnabled() && it.isLogToProto())
+                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+                + "\n  Logcat: "
+                + LOG_GROUPS.values().stream().filter(
+                    it -> it.isEnabled() && it.isLogToLogcat())
+                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+                + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
+    }
+
+    /**
+     * Writes the log buffer to a new file for the bugreport.
+     *
+     * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
+     * {@link #stopProtoLog(PrintWriter, boolean)}.
+     */
+    public void writeProtoLogToFile() {
+        synchronized (mProtoLogEnabledLock) {
+            writeProtoLogToFileLocked();
+        }
+    }
+
+    private void writeProtoLogToFileLocked() {
+        try {
+            long offset =
+                    (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
+            ProtoOutputStream proto = new ProtoOutputStream();
+            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+            proto.write(VERSION, PROTOLOG_VERSION);
+            proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
+            mBuffer.writeTraceToFile(mLogFile, proto);
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to write buffer to file", e);
+        }
+    }
+
+    static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Slog.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+}
+
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 0834b2d..9f7436a 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -70,6 +70,8 @@
             Consts.TAG_WM),
     WM_DEBUG_IME(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
+    WM_DEBUG_WINDOW_ORGANIZER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
     TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
 
     private final boolean mEnabled;
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 6874f10..10224a4 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -16,58 +16,22 @@
 
 package com.android.internal.protolog;
 
-import static com.android.internal.protolog.ProtoLogFileProto.LOG;
-import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER;
-import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
-import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
-import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
-import static com.android.internal.protolog.ProtoLogFileProto.VERSION;
-import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
-import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS;
-import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
-import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH;
-import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
-import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
-
 import android.annotation.Nullable;
-import android.os.ShellCommand;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.IProtoLogGroup;
-import com.android.internal.protolog.common.LogDataType;
-import com.android.internal.util.TraceBuffer;
 
 import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.IllegalFormatConversionException;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
-
 
 /**
  * A service for the ProtoLog logging system.
  */
-public class ProtoLogImpl {
-    private static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
+public class ProtoLogImpl extends BaseProtoLogImpl {
+    private static final int BUFFER_CAPACITY = 1024 * 1024;
+    private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
+    private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
 
-    /**
-     * A runnable to update the cached output of {@link #isEnabled}.
-     *
-     * Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
-     * starting / stopping proto log, or enabling / disabling log groups.
-     */
-    public static Runnable sCacheUpdater = () -> { };
-
-    private static void addLogGroupEnum(IProtoLogGroup[] config) {
-        for (IProtoLogGroup group : config) {
-            LOG_GROUPS.put(group.name(), group);
-        }
-    }
+    private static ProtoLogImpl sServiceInstance = null;
 
     static {
         addLogGroupEnum(ProtoLogGroup.values());
@@ -124,30 +88,13 @@
                 || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
     }
 
-    private static final int BUFFER_CAPACITY = 1024 * 1024;
-    private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
-    private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
-    private static final String TAG = "ProtoLog";
-    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
-    static final String PROTOLOG_VERSION = "1.0.0";
-
-    private final File mLogFile;
-    private final TraceBuffer mBuffer;
-    private final ProtoLogViewerConfigReader mViewerConfig;
-
-    private boolean mProtoLogEnabled;
-    private boolean mProtoLogEnabledLockFree;
-    private final Object mProtoLogEnabledLock = new Object();
-
-    private static ProtoLogImpl sServiceInstance = null;
-
     /**
      * Returns the single instance of the ProtoLogImpl singleton class.
      */
     public static synchronized ProtoLogImpl getSingleInstance() {
         if (sServiceInstance == null) {
-            sServiceInstance = new ProtoLogImpl(new File(LOG_FILENAME), BUFFER_CAPACITY,
-                    new ProtoLogViewerConfigReader());
+            sServiceInstance = new ProtoLogImpl(
+                    new File(LOG_FILENAME), BUFFER_CAPACITY, new ProtoLogViewerConfigReader());
         }
         return sServiceInstance;
     }
@@ -157,307 +104,9 @@
         sServiceInstance = instance;
     }
 
-    @VisibleForTesting
-    public enum LogLevel {
-        DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
-    }
-
-    /**
-     * Main log method, do not call directly.
-     */
-    @VisibleForTesting
-    public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
-            @Nullable String messageString, Object[] args) {
-        if (group.isLogToProto()) {
-            logToProto(messageHash, paramsMask, args);
-        }
-        if (group.isLogToLogcat()) {
-            logToLogcat(group.getTag(), level, messageHash, messageString, args);
-        }
-    }
-
-    private void logToLogcat(String tag, LogLevel level, int messageHash,
-            @Nullable String messageString, Object[] args) {
-        String message = null;
-        if (messageString == null) {
-            messageString = mViewerConfig.getViewerString(messageHash);
-        }
-        if (messageString != null) {
-            try {
-                message = String.format(messageString, args);
-            } catch (IllegalFormatConversionException ex) {
-                Slog.w(TAG, "Invalid ProtoLog format string.", ex);
-            }
-        }
-        if (message == null) {
-            StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
-            for (Object o : args) {
-                builder.append(" ").append(o);
-            }
-            message = builder.toString();
-        }
-        passToLogcat(tag, level, message);
-    }
-
-    /**
-     * SLog wrapper.
-     */
-    @VisibleForTesting
-    public void passToLogcat(String tag, LogLevel level, String message) {
-        switch (level) {
-            case DEBUG:
-                Slog.d(tag, message);
-                break;
-            case VERBOSE:
-                Slog.v(tag, message);
-                break;
-            case INFO:
-                Slog.i(tag, message);
-                break;
-            case WARN:
-                Slog.w(tag, message);
-                break;
-            case ERROR:
-                Slog.e(tag, message);
-                break;
-            case WTF:
-                Slog.wtf(tag, message);
-                break;
-        }
-    }
-
-    private void logToProto(int messageHash, int paramsMask, Object[] args) {
-        if (!isProtoEnabled()) {
-            return;
-        }
-        try {
-            ProtoOutputStream os = new ProtoOutputStream();
-            long token = os.start(LOG);
-            os.write(MESSAGE_HASH, messageHash);
-            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-
-            if (args != null) {
-                int argIndex = 0;
-                ArrayList<Long> longParams = new ArrayList<>();
-                ArrayList<Double> doubleParams = new ArrayList<>();
-                ArrayList<Boolean> booleanParams = new ArrayList<>();
-                for (Object o : args) {
-                    int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
-                    try {
-                        switch (type) {
-                            case LogDataType.STRING:
-                                os.write(STR_PARAMS, o.toString());
-                                break;
-                            case LogDataType.LONG:
-                                longParams.add(((Number) o).longValue());
-                                break;
-                            case LogDataType.DOUBLE:
-                                doubleParams.add(((Number) o).doubleValue());
-                                break;
-                            case LogDataType.BOOLEAN:
-                                booleanParams.add((boolean) o);
-                                break;
-                        }
-                    } catch (ClassCastException ex) {
-                        // Should not happen unless there is an error in the ProtoLogTool.
-                        os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
-                        Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
-                    }
-                    argIndex++;
-                }
-                if (longParams.size() > 0) {
-                    os.writePackedSInt64(SINT64_PARAMS,
-                            longParams.stream().mapToLong(i -> i).toArray());
-                }
-                if (doubleParams.size() > 0) {
-                    os.writePackedDouble(DOUBLE_PARAMS,
-                            doubleParams.stream().mapToDouble(i -> i).toArray());
-                }
-                if (booleanParams.size() > 0) {
-                    boolean[] arr = new boolean[booleanParams.size()];
-                    for (int i = 0; i < booleanParams.size(); i++) {
-                        arr[i] = booleanParams.get(i);
-                    }
-                    os.writePackedBool(BOOLEAN_PARAMS, arr);
-                }
-            }
-            os.end(token);
-            mBuffer.add(os);
-        } catch (Exception e) {
-            Slog.e(TAG, "Exception while logging to proto", e);
-        }
-    }
-
-    public ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) {
-        mLogFile = file;
-        mBuffer = new TraceBuffer(bufferCapacity);
-        mViewerConfig = viewerConfig;
-    }
-
-    /**
-     * Starts the logging a circular proto buffer.
-     *
-     * @param pw Print writer
-     */
-    public void startProtoLog(@Nullable PrintWriter pw) {
-        if (isProtoEnabled()) {
-            return;
-        }
-        synchronized (mProtoLogEnabledLock) {
-            logAndPrintln(pw, "Start logging to " + mLogFile + ".");
-            mBuffer.resetBuffer();
-            mProtoLogEnabled = true;
-            mProtoLogEnabledLockFree = true;
-        }
-        sCacheUpdater.run();
-    }
-
-    /**
-     * Stops logging to proto.
-     *
-     * @param pw          Print writer
-     * @param writeToFile If the current buffer should be written to disk or not
-     */
-    public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
-        if (!isProtoEnabled()) {
-            return;
-        }
-        synchronized (mProtoLogEnabledLock) {
-            logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
-            mProtoLogEnabled = mProtoLogEnabledLockFree = false;
-            if (writeToFile) {
-                writeProtoLogToFileLocked();
-                logAndPrintln(pw, "Log written to " + mLogFile + ".");
-            }
-            if (mProtoLogEnabled) {
-                logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
-                throw new IllegalStateException("logging enabled while waiting for flush.");
-            }
-        }
-        sCacheUpdater.run();
-    }
-
-    /**
-     * Returns {@code true} iff logging to proto is enabled.
-     */
-    public boolean isProtoEnabled() {
-        return mProtoLogEnabledLockFree;
-    }
-
-    private int setLogging(ShellCommand shell, boolean setTextLogging, boolean value) {
-        String group;
-        while ((group = shell.getNextArg()) != null) {
-            IProtoLogGroup g = LOG_GROUPS.get(group);
-            if (g != null) {
-                if (setTextLogging) {
-                    g.setLogToLogcat(value);
-                } else {
-                    g.setLogToProto(value);
-                }
-            } else {
-                logAndPrintln(shell.getOutPrintWriter(), "No IProtoLogGroup named " + group);
-                return -1;
-            }
-        }
-        sCacheUpdater.run();
-        return 0;
-    }
-
-    private int unknownCommand(PrintWriter pw) {
-        pw.println("Unknown command");
-        pw.println("Window manager logging options:");
-        pw.println("  start: Start proto logging");
-        pw.println("  stop: Stop proto logging");
-        pw.println("  enable [group...]: Enable proto logging for given groups");
-        pw.println("  disable [group...]: Disable proto logging for given groups");
-        pw.println("  enable-text [group...]: Enable logcat logging for given groups");
-        pw.println("  disable-text [group...]: Disable logcat logging for given groups");
-        return -1;
-    }
-
-    /**
-     * Responds to a shell command.
-     */
-    public int onShellCommand(ShellCommand shell) {
-        PrintWriter pw = shell.getOutPrintWriter();
-        String cmd = shell.getNextArg();
-        if (cmd == null) {
-            return unknownCommand(pw);
-        }
-        switch (cmd) {
-            case "start":
-                startProtoLog(pw);
-                return 0;
-            case "stop":
-                stopProtoLog(pw, true);
-                return 0;
-            case "status":
-                logAndPrintln(pw, getStatus());
-                return 0;
-            case "enable":
-                return setLogging(shell, false, true);
-            case "enable-text":
-                mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME);
-                return setLogging(shell, true, true);
-            case "disable":
-                return setLogging(shell, false, false);
-            case "disable-text":
-                return setLogging(shell, true, false);
-            default:
-                return unknownCommand(pw);
-        }
-    }
-
-    /**
-     * Returns a human-readable ProtoLog status text.
-     */
-    public String getStatus() {
-        return "ProtoLog status: "
-                + ((isProtoEnabled()) ? "Enabled" : "Disabled")
-                + "\nEnabled log groups: \n  Proto: "
-                + LOG_GROUPS.values().stream().filter(
-                    it -> it.isEnabled() && it.isLogToProto())
-                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
-                + "\n  Logcat: "
-                + LOG_GROUPS.values().stream().filter(
-                    it -> it.isEnabled() && it.isLogToLogcat())
-                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
-                + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
-    }
-
-    /**
-     * Writes the log buffer to a new file for the bugreport.
-     *
-     * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
-     * {@link #stopProtoLog(PrintWriter, boolean)}.
-     */
-    public void writeProtoLogToFile() {
-        synchronized (mProtoLogEnabledLock) {
-            writeProtoLogToFileLocked();
-        }
-    }
-
-    private void writeProtoLogToFileLocked() {
-        try {
-            long offset =
-                    (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
-            ProtoOutputStream proto = new ProtoOutputStream();
-            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
-            proto.write(VERSION, PROTOLOG_VERSION);
-            proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
-            mBuffer.writeTraceToFile(mLogFile, proto);
-        } catch (IOException e) {
-            Slog.e(TAG, "Unable to write buffer to file", e);
-        }
-    }
-
-
-    static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
-        Slog.i(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
+    public ProtoLogImpl(File logFile, int bufferCapacity,
+            ProtoLogViewerConfigReader viewConfigReader) {
+        super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader);
     }
 }
 
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index e381d30..aa30a77 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.protolog;
 
+import android.annotation.Nullable;
+import android.util.Slog;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -23,6 +26,7 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.Iterator;
@@ -34,6 +38,7 @@
  * Handles loading and parsing of ProtoLog viewer configuration.
  */
 public class ProtoLogViewerConfigReader {
+    private static final String TAG = "ProtoLogViewerConfigReader";
     private Map<Integer, String> mLogMessageMap = null;
 
     /** Returns message format string for its hash or null if unavailable. */
@@ -49,48 +54,54 @@
      * Reads the specified viewer configuration file. Does nothing if the config is already loaded.
      */
     public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) {
+        try {
+            loadViewerConfig(new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
+            logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
+                    + " log definitions from " + viewerConfigFilename);
+        } catch (FileNotFoundException e) {
+            logAndPrintln(pw, "Unable to load log definitions: File "
+                    + viewerConfigFilename + " not found." + e);
+        } catch (IOException e) {
+            logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
+                    + viewerConfigFilename + ". " + e);
+        } catch (JSONException e) {
+            logAndPrintln(pw, "Unable to load log definitions: JSON parsing exception while reading "
+                    + viewerConfigFilename + ". " + e);
+        }
+    }
+
+    /**
+     * Reads the specified viewer configuration input stream.
+     * Does nothing if the config is already loaded.
+     */
+    public synchronized void loadViewerConfig(InputStream viewerConfigInputStream)
+            throws IOException, JSONException {
         if (mLogMessageMap != null) {
             return;
         }
-        try {
-            InputStreamReader config = new InputStreamReader(
-                    new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
-            BufferedReader reader = new BufferedReader(config);
-            StringBuilder builder = new StringBuilder();
-            String line;
-            while ((line = reader.readLine()) != null) {
-                builder.append(line).append('\n');
-            }
-            reader.close();
-            JSONObject json = new JSONObject(builder.toString());
-            JSONObject messages = json.getJSONObject("messages");
+        InputStreamReader config = new InputStreamReader(viewerConfigInputStream);
+        BufferedReader reader = new BufferedReader(config);
+        StringBuilder builder = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            builder.append(line).append('\n');
+        }
+        reader.close();
+        JSONObject json = new JSONObject(builder.toString());
+        JSONObject messages = json.getJSONObject("messages");
 
-            mLogMessageMap = new TreeMap<>();
-            Iterator it = messages.keys();
-            while (it.hasNext()) {
-                String key = (String) it.next();
-                try {
-                    int hash = Integer.parseInt(key);
-                    JSONObject val = messages.getJSONObject(key);
-                    String msg = val.getString("message");
-                    mLogMessageMap.put(hash, msg);
-                } catch (NumberFormatException expected) {
-                    // Not a messageHash - skip it
-                }
+        mLogMessageMap = new TreeMap<>();
+        Iterator it = messages.keys();
+        while (it.hasNext()) {
+            String key = (String) it.next();
+            try {
+                int hash = Integer.parseInt(key);
+                JSONObject val = messages.getJSONObject(key);
+                String msg = val.getString("message");
+                mLogMessageMap.put(hash, msg);
+            } catch (NumberFormatException expected) {
+                // Not a messageHash - skip it
             }
-            ProtoLogImpl.logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
-                    + " log definitions from " + viewerConfigFilename);
-        } catch (FileNotFoundException e) {
-            ProtoLogImpl.logAndPrintln(pw, "Unable to load log definitions: File "
-                    + viewerConfigFilename + " not found." + e);
-        } catch (IOException e) {
-            ProtoLogImpl.logAndPrintln(pw,
-                    "Unable to load log definitions: IOException while reading "
-                    + viewerConfigFilename + ". " + e);
-        } catch (JSONException e) {
-            ProtoLogImpl.logAndPrintln(pw,
-                    "Unable to load log definitions: JSON parsing exception while reading "
-                            + viewerConfigFilename + ". " + e);
         }
     }
 
@@ -103,4 +114,12 @@
         }
         return 0;
     }
+
+    static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Slog.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index d41d307..25d1ae6 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -23,6 +23,7 @@
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.PhoneCapability;
+import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
 import android.telephony.ServiceState;
@@ -68,4 +69,6 @@
     void onRegistrationFailed(in CellIdentity cellIdentity,
              String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
     void onBarringInfoChanged(in BarringInfo barringInfo);
+    void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
+
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index ea09fc8..313bd42 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -47,10 +47,10 @@
       */
     @UnsupportedAppUsage
     void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
-    void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, int events,
+    void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, long events,
             boolean notifyNow);
     void listenForSubscriber(in int subId, String pkg, String featureId,
-            IPhoneStateListener callback, int events, boolean notifyNow);
+            IPhoneStateListener callback, long events, boolean notifyNow);
     @UnsupportedAppUsage
     void notifyCallStateForAllSubs(int state, String incomingNumber);
     void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber);
@@ -99,4 +99,6 @@
     void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
             String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
     void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo);
+    void notifyPhysicalChannelConfigurationForSubscriber(in int subId,
+            in List<PhysicalChannelConfig> configs);
 }
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 67cfc3a..555f62c 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -14,13 +14,9 @@
 
 package com.android.internal.util;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.Build;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.EventLog;
 import android.util.Log;
@@ -29,18 +25,14 @@
 import com.android.internal.logging.EventLogTags;
 
 /**
- * Class to track various latencies in SystemUI. It then outputs the latency to logcat so these
- * latencies can be captured by tests and then used for dashboards.
+ * Class to track various latencies in SystemUI. It then writes the latency to statsd and also
+ * outputs it to logcat so these latencies can be captured by tests and then used for dashboards.
  * <p>
  * This is currently only in Keyguard so it can be shared between SystemUI and Keyguard, but
  * eventually we'd want to merge these two packages together so Keyguard can use common classes
  * that are shared with SystemUI.
  */
 public class LatencyTracker {
-
-    private static final String ACTION_RELOAD_PROPERTY =
-            "com.android.systemui.RELOAD_LATENCY_TRACKER_PROPERTY";
-
     private static final String TAG = "LatencyTracker";
 
     /**
@@ -82,7 +74,7 @@
     /*
      * Time between we get a face acquired signal until we start with the unlock animation
      */
-    public static final int ACTION_FACE_WAKE_AND_UNLOCK = 6;
+    public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
 
     private static final String[] NAMES = new String[] {
             "expand panel",
@@ -94,38 +86,34 @@
             "rotate the screen",
             "face wake-and-unlock" };
 
+    private static final int[] STATSD_ACTION = new int[] {
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
+            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
+    };
+
     private static LatencyTracker sLatencyTracker;
 
     private final SparseLongArray mStartRtc = new SparseLongArray();
-    private boolean mEnabled;
 
     public static LatencyTracker getInstance(Context context) {
         if (sLatencyTracker == null) {
-            sLatencyTracker = new LatencyTracker(context.getApplicationContext());
+            sLatencyTracker = new LatencyTracker();
         }
         return sLatencyTracker;
     }
 
-    private LatencyTracker(Context context) {
-        context.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                reloadProperty();
-            }
-        }, new IntentFilter(ACTION_RELOAD_PROPERTY));
-        reloadProperty();
-    }
-
-    private void reloadProperty() {
-        mEnabled = SystemProperties.getBoolean("debug.systemui.latency_tracking", false);
-    }
-
     public static boolean isEnabled(Context ctx) {
         return getInstance(ctx).isEnabled();
     }
 
     public boolean isEnabled() {
-        return Build.IS_DEBUGGABLE && mEnabled;
+        return Build.IS_DEBUGGABLE;
     }
 
     /**
@@ -134,7 +122,7 @@
      * @param action The action to start. One of the ACTION_* values.
      */
     public void onActionStart(int action) {
-        if (!mEnabled) {
+        if (!isEnabled()) {
             return;
         }
         Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, NAMES[action], 0);
@@ -147,7 +135,7 @@
      * @param action The action to end. One of the ACTION_* values.
      */
     public void onActionEnd(int action) {
-        if (!mEnabled) {
+        if (!isEnabled()) {
             return;
         }
         long endRtc = SystemClock.elapsedRealtime();
@@ -169,5 +157,7 @@
     public static void logAction(int action, int duration) {
         Log.i(TAG, "action=" + action + " latency=" + duration);
         EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
     }
 }
diff --git a/core/java/com/android/internal/util/StatLogger.java b/core/java/com/android/internal/util/StatLogger.java
index 2d65090..1622f7d 100644
--- a/core/java/com/android/internal/util/StatLogger.java
+++ b/core/java/com/android/internal/util/StatLogger.java
@@ -17,6 +17,7 @@
 package com.android.internal.util;
 
 import android.os.SystemClock;
+import android.text.TextUtils;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -65,8 +66,14 @@
     private long mNextTickTime = SystemClock.elapsedRealtime() + 1000;
 
     private final String[] mLabels;
+    private final String mStatsTag;
 
     public StatLogger(String[] eventLabels) {
+        this(null, eventLabels);
+    }
+
+    public StatLogger(String statsTag, String[] eventLabels) {
+        mStatsTag = statsTag;
         SIZE = eventLabels.length;
         mCountStats = new int[SIZE];
         mDurationStats = new long[SIZE];
@@ -135,7 +142,11 @@
 
     public void dump(IndentingPrintWriter pw) {
         synchronized (mLock) {
-            pw.println("Stats:");
+            if (!TextUtils.isEmpty(mStatsTag)) {
+                pw.println(mStatsTag + ":");
+            } else {
+                pw.println("Stats:");
+            }
             pw.increaseIndent();
             for (int i = 0; i < SIZE; i++) {
                 final int count = mCountStats[i];
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
index 9f100bd..a92e978 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
@@ -26,6 +26,36 @@
     int DOWN = 1;
 
     /**
+     * Contains the result of a scroll request.
+     */
+    class ScrollResult {
+        /**
+         * The area requested in pixels, within {@link #onComputeScrollBounds scroll bounds}, with
+         * top/bottom relative to the scroll position at the start of capture.
+         */
+        public Rect requestedArea;
+        /**
+         * The area, in pixels of the request which is visible and available for capture. In the
+         * same coordinate space as {@link #requestedArea}.
+         */
+        public Rect availableArea;
+        /**
+         * The updated scroll delta (the relative distance, in pixels that the scroll position has
+         * moved from the starting position since capture started).
+         */
+        public int scrollDelta; // visible top offset from start
+
+        @Override
+        public String toString() {
+            return "ScrollResult{"
+                    + "requestedArea=" + requestedArea
+                    + ", availableArea=" + availableArea
+                    + ", scrollDelta=" + scrollDelta
+                    + '}';
+        }
+    }
+
+    /**
      * Verifies that the view is still visible and scrollable. If true is returned here, expect a
      * call to {@link #onComputeScrollBounds(View)} to follow.
      *
@@ -48,6 +78,7 @@
                 view.getWidth() - view.getPaddingRight(),
                 view.getHeight() - view.getPaddingBottom());
     }
+
     /**
      * Adjust the target for capture.
      * <p>
@@ -67,14 +98,14 @@
      * needed and return the resulting rectangle describing the position and bounds of the area
      * which is visible.
      *
+     * @param view the view being captured
      * @param scrollBounds the area in which scrolling content moves, local to the {@code containing
      *                     view}
      * @param requestRect  the area relative to {@code scrollBounds} which describes the location of
      *                     content to capture for the request
-     * @return the visible area within scrollBounds of the requested rectangle, return {@code null}
-     * in the case of an unrecoverable error condition, to abort the capture process
+     * @return the result of the request as a {@link ScrollResult}
      */
-    Rect onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect);
+    ScrollResult onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect);
 
     /**
      * Restore the target after capture.
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 4087eda..7b4f73f 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -30,21 +30,24 @@
 import android.view.Surface;
 import android.view.View;
 
+import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
+
 import java.lang.ref.WeakReference;
 import java.util.function.Consumer;
 
 /**
- * Provides a ScrollCaptureCallback implementation for to handle arbitrary View-based scrolling
- * containers.
- * <p>
- * To use this class, supply the target view and an implementation of {@ScrollCaptureViewHelper}
- * to the callback.
+ * Provides a base ScrollCaptureCallback implementation to handle arbitrary View-based scrolling
+ * containers. This class handles the bookkeeping aspects of {@link ScrollCaptureCallback}
+ * including rendering output using HWUI. Adaptable to any {@link View} using
+ * {@link ScrollCaptureViewHelper}.
  *
  * @param <V> the specific View subclass handled
- * @hide
+ * @see ScrollCaptureViewHelper
  */
 public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
 
+    private static final String TAG = "ScrollCaptureViewSupport";
+
     private final WeakReference<V> mWeakView;
     private final ScrollCaptureViewHelper<V> mViewHelper;
     private ViewRenderer mRenderer;
@@ -52,11 +55,6 @@
     private boolean mStarted;
     private boolean mEnded;
 
-    static <V extends View> ScrollCaptureCallback createCallback(V view,
-            ScrollCaptureViewHelper<V> impl) {
-        return new ScrollCaptureViewSupport<>(view, impl);
-    }
-
     ScrollCaptureViewSupport(V containingView, ScrollCaptureViewHelper<V> viewHelper) {
         mWeakView = new WeakReference<>(containingView);
         mRenderer = new ViewRenderer();
@@ -82,6 +80,7 @@
     @Override
     public final void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) {
         V view = mWeakView.get();
+
         mEnded = false;
         mStarted = true;
 
@@ -103,21 +102,30 @@
             session.notifyBufferSent(0, null);
             return;
         }
-        Rect captureArea = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
+        // Ask the view to scroll as needed to bring this area into view.
+        ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
                 requestRect);
-        mRenderer.renderFrame(view, captureArea, mUiHandler,
-                () -> session.notifyBufferSent(0, captureArea));
+        view.invalidate(); // don't wait for vsync
+
+        // For image capture, shift back by scrollDelta to arrive at the location within the view
+        // where the requested content will be drawn
+        Rect viewCaptureArea = new Rect(scrollResult.availableArea);
+        viewCaptureArea.offset(0, -scrollResult.scrollDelta);
+
+        mRenderer.renderView(view, viewCaptureArea, mUiHandler,
+                (frameNumber) -> session.notifyBufferSent(frameNumber, scrollResult.availableArea));
     }
 
     @Override
     public final void onScrollCaptureEnd(Runnable onReady) {
         V view = mWeakView.get();
         if (mStarted && !mEnded) {
-            mViewHelper.onPrepareForEnd(view);
-            /* empty */
+            if (view != null) {
+                mViewHelper.onPrepareForEnd(view);
+                view.invalidate();
+            }
             mEnded = true;
-            mRenderer.trimMemory();
-            mRenderer.setSurface(null);
+            mRenderer.destroy();
         }
         onReady.run();
     }
@@ -142,7 +150,7 @@
         private static final String TAG = "ViewRenderer";
 
         private HardwareRenderer mRenderer;
-        private RenderNode mRootRenderNode;
+        private RenderNode mCaptureRenderNode;
         private final RectF mTempRectF = new RectF();
         private final Rect mSourceRect = new Rect();
         private final Rect mTempRect = new Rect();
@@ -151,10 +159,14 @@
         private long mLastRenderedSourceDrawingId = -1;
 
 
+        public interface FrameCompleteListener {
+            void onFrameComplete(long frameNumber);
+        }
+
         ViewRenderer() {
             mRenderer = new HardwareRenderer();
-            mRootRenderNode = new RenderNode("ScrollCaptureRoot");
-            mRenderer.setContentRoot(mRootRenderNode);
+            mCaptureRenderNode = new RenderNode("ScrollCaptureRoot");
+            mRenderer.setContentRoot(mCaptureRenderNode);
 
             // TODO: Figure out a way to flip this on when we are sure the source window is opaque
             mRenderer.setOpaque(false);
@@ -193,18 +205,36 @@
             // Enable shadows for elevation/Z
             mRenderer.setLightSourceGeometry(lightX, lightY, lightZ, lightRadius);
             mRenderer.setLightSourceAlpha(AMBIENT_SHADOW_ALPHA, SPOT_SHADOW_ALPHA);
-
         }
 
-        public void renderFrame(View localReference, Rect sourceRect, Handler handler,
-                Runnable onFrameCommitted) {
-            if (updateForView(localReference)) {
-                setupLighting(localReference);
+        private void updateRootNode(View source, Rect localSourceRect) {
+            final View rootView = source.getRootView();
+            transformToRoot(source, localSourceRect, mTempRect);
+
+            mCaptureRenderNode.setPosition(0, 0, mTempRect.width(), mTempRect.height());
+            RecordingCanvas canvas = mCaptureRenderNode.beginRecording();
+            canvas.enableZ();
+            canvas.translate(-mTempRect.left, -mTempRect.top);
+
+            RenderNode rootViewRenderNode = rootView.updateDisplayListIfDirty();
+            if (rootViewRenderNode.hasDisplayList()) {
+                canvas.drawRenderNode(rootViewRenderNode);
             }
-            buildRootDisplayList(localReference, sourceRect);
+            mCaptureRenderNode.endRecording();
+        }
+
+        public void renderView(View view, Rect sourceRect, Handler handler,
+                FrameCompleteListener frameListener) {
+            if (updateForView(view)) {
+                setupLighting(view);
+            }
+            view.invalidate();
+            updateRootNode(view, sourceRect);
             HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest();
             request.setVsyncTime(SystemClock.elapsedRealtimeNanos());
-            request.setFrameCommitCallback(handler::post, onFrameCommitted);
+            // private API b/c request.setFrameCommitCallback does not provide access to frameNumber
+            mRenderer.setFrameCompleteCallback(
+                    frameNr -> handler.post(() -> frameListener.onFrameComplete(frameNr)));
             request.setWaitForPresent(true);
             request.syncAndDraw();
         }
@@ -225,15 +255,5 @@
             mTempRectF.round(outRect);
         }
 
-        private void buildRootDisplayList(View source, Rect localSourceRect) {
-            final View captureSource = source.getRootView();
-            transformToRoot(source, localSourceRect, mTempRect);
-            mRootRenderNode.setPosition(0, 0, mTempRect.width(), mTempRect.height());
-            RecordingCanvas canvas = mRootRenderNode.beginRecording(mTempRect.width(),
-                    mTempRect.height());
-            canvas.translate(-mTempRect.left, -mTempRect.top);
-            canvas.drawRenderNode(captureSource.updateDisplayListIfDirty());
-            mRootRenderNode.endRecording();
-        }
     }
 }
diff --git a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
index 12bd461..1514b9a 100644
--- a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
@@ -35,13 +35,14 @@
  * <li>correctly implements {@link ViewParent#requestChildRectangleOnScreen(View,
  * Rect, boolean)}
  * </ul>
+ *
+ * @see ScrollCaptureViewSupport
  */
 public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> {
     private int mStartScrollY;
     private boolean mScrollBarEnabled;
     private int mOverScrollMode;
 
-    /** @see ScrollCaptureViewHelper#onPrepareForStart(View, Rect) */
     public void onPrepareForStart(@NonNull ViewGroup view, Rect scrollBounds) {
         mStartScrollY = view.getScrollY();
         mOverScrollMode = view.getOverScrollMode();
@@ -54,8 +55,8 @@
         }
     }
 
-    /** @see ScrollCaptureViewHelper#onScrollRequested(View, Rect, Rect) */
-    public Rect onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds, Rect requestRect) {
+    public ScrollResult onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds,
+            Rect requestRect) {
         final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
         if (contentView == null) {
             return null;
@@ -87,6 +88,9 @@
             \__ Requested Bounds[0,300 - 200,400] (200x100)
        */
 
+        ScrollResult result = new ScrollResult();
+        result.requestedArea = new Rect(requestRect);
+
         // 0) adjust the requestRect to account for scroll change since start
         //
         //  Scroll Bounds[50,50 - 250,250]  (w=200,h=200)
@@ -117,8 +121,6 @@
                 view.getScrollX() - contentView.getLeft(),
                 view.getScrollY() - contentView.getTop());
 
-
-
         // requestRect is now local to contentView as requestedContentBounds
         // contentView (and each parent in turn if possible) will be scrolled
         // (if necessary) to make all of requestedContent visible, (if possible!)
@@ -126,35 +128,37 @@
 
         // update new offset between starting and current scroll position
         scrollDelta = view.getScrollY() - mStartScrollY;
+        result.scrollDelta = scrollDelta;
 
-
-        // TODO: adjust to avoid occlusions/minimize scroll changes
+        // TODO: crop capture area to avoid occlusions/minimize scroll changes
 
         Point offset = new Point();
-        final Rect capturedRect = new Rect(requestedContentBounds); // empty
-        if (!view.getChildVisibleRect(contentView, capturedRect, offset)) {
-            capturedRect.setEmpty();
-            return capturedRect;
+        final Rect available = new Rect(requestedContentBounds); // empty
+        if (!view.getChildVisibleRect(contentView, available, offset)) {
+            available.setEmpty();
+            result.availableArea = available;
+            return result;
         }
         // Transform back from global to content-view local
-        capturedRect.offset(-offset.x, -offset.y);
+        available.offset(-offset.x, -offset.y);
 
         // Then back to container view
-        capturedRect.offset(
+        available.offset(
                 contentView.getLeft() - view.getScrollX(),
                 contentView.getTop() - view.getScrollY());
 
 
         // And back to relative to scrollBounds
-        capturedRect.offset(-scrollBounds.left, -scrollBounds.top);
+        available.offset(-scrollBounds.left, -scrollBounds.top);
 
-        // Apply scrollDelta again to return to make capturedRect relative to scrollBounds at
+        // Apply scrollDelta again to return to make `available` relative to `scrollBounds` at
         // the scroll position at start of capture.
-        capturedRect.offset(0, scrollDelta);
-        return capturedRect;
+        available.offset(0, scrollDelta);
+
+        result.availableArea = new Rect(available);
+        return result;
     }
 
-    /** @see ScrollCaptureViewHelper#onPrepareForEnd(View)  */
     public void onPrepareForEnd(@NonNull ViewGroup view) {
         view.scrollTo(0, mStartScrollY);
         if (mOverScrollMode != View.OVER_SCROLL_NEVER) {
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 9ad4cd9..859b40a 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -501,6 +501,15 @@
                              "Failed to read from parcel (error code %d)", err);
         return;
     }
+
+    // Update vendor descriptor cache if necessary
+    auto vendorId = metadata->getVendorId();
+    if ((vendorId != CAMERA_METADATA_INVALID_VENDOR_ID) &&
+            !VendorTagDescriptorCache::isVendorCachePresent(vendorId)) {
+        ALOGW("%s: Tag vendor id missing or cache not initialized, trying to update!",
+                __FUNCTION__);
+        CameraMetadata_setupGlobalVendorTagDescriptor(env, thiz);
+    }
 }
 
 static void CameraMetadata_writeToParcel(JNIEnv *env, jclass thiz, jobject parcel, jlong ptr) {
@@ -642,9 +651,7 @@
     CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(ptr);
     metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
     if (metadata) {
-        const camera_metadata_t *metaBuffer = metadata->getAndLock();
-        vendorId = get_camera_metadata_vendor_id(metaBuffer);
-        metadata->unlock(metaBuffer);
+        vendorId = metadata->getVendorId();
     }
 
     int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
@@ -673,9 +680,7 @@
     if (metadata) {
         sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
         if (cache.get()) {
-            const camera_metadata_t *metaBuffer = metadata->getAndLock();
-            metadata_vendor_id_t vendorId = get_camera_metadata_vendor_id(metaBuffer);
-            metadata->unlock(metaBuffer);
+            auto vendorId = metadata->getVendorId();
             cache->getVendorTagDescriptor(vendorId, &vTags);
         }
     }
@@ -703,10 +708,8 @@
         CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, ptr);
         if (metadata == NULL) return NULL;
 
-        const camera_metadata_t *metaBuffer = metadata->getAndLock();
-        vendorId = get_camera_metadata_vendor_id(metaBuffer);
+        vendorId = metadata->getVendorId();
         cache->getVendorTagDescriptor(vendorId, &vTags);
-        metadata->unlock(metaBuffer);
         if (vTags.get() == nullptr) {
             return nullptr;
         }
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 3f39478..5c4c509 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2407,6 +2407,79 @@
     return AUDIO_JAVA_SUCCESS;
 }
 
+static jint android_media_AudioSystem_setDevicesRoleForCapturePreset(
+        JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+        jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
+    int status = check_AudioSystem_Command(
+            AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                        (device_role_t)role, nDevices));
+    return (jint)status;
+}
+
+static jint android_media_AudioSystem_addDevicesRoleForCapturePreset(
+        JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+        jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
+    int status = check_AudioSystem_Command(
+            AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                        (device_role_t)role, nDevices));
+    return (jint)status;
+}
+
+static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset(
+        JNIEnv *env, jobject thiz, jint capturePreset, jint role, jintArray jDeviceTypes,
+        jobjectArray jDeviceAddresses) {
+    AudioDeviceTypeAddrVector nDevices;
+    jint results = getVectorOfAudioDeviceTypeAddr(env, jDeviceTypes, jDeviceAddresses, nDevices);
+    if (results != NO_ERROR) {
+        return results;
+    }
+    int status = check_AudioSystem_Command(
+            AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                           (device_role_t)role, nDevices));
+    return (jint)status;
+}
+
+static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz,
+                                                                       jint capturePreset,
+                                                                       jint role) {
+    return (jint)check_AudioSystem_Command(
+            AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset,
+                                                          (device_role_t)role));
+}
+
+static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz,
+                                                                        jint capturePreset,
+                                                                        jint role,
+                                                                        jobject jDevices) {
+    AudioDeviceTypeAddrVector nDevices;
+    status_t status = check_AudioSystem_Command(
+            AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset,
+                                                           (device_role_t)role, nDevices));
+    if (status != NO_ERROR) {
+        return (jint)status;
+    }
+    for (const auto &device : nDevices) {
+        jobject jAudioDeviceAttributes = NULL;
+        jint jStatus = createAudioDeviceAttributesFromNative(env, &jAudioDeviceAttributes, &device);
+        if (jStatus != AUDIO_JAVA_SUCCESS) {
+            return jStatus;
+        }
+        env->CallBooleanMethod(jDevices, gListMethods.add, jAudioDeviceAttributes);
+        env->DeleteLocalRef(jAudioDeviceAttributes);
+    }
+    return AUDIO_JAVA_SUCCESS;
+}
+
 static jint
 android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
         jobject jaa, jobjectArray jDeviceArray)
@@ -2558,6 +2631,16 @@
           (void *)android_media_AudioSystem_removeDevicesRoleForStrategy},
          {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I",
           (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy},
+         {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset},
+         {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset},
+         {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset},
+         {"clearDevicesRoleForCapturePreset", "(II)I",
+          (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset},
+         {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
+          (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset},
          {"getDevicesForAttributes",
           "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
           (void *)android_media_AudioSystem_getDevicesForAttributes},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 5c045b6..7a5c383 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -20,6 +20,7 @@
 #include "android_media_AudioTrack.h"
 
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include "core_jni_helpers.h"
 
 #include <utils/Log.h>
@@ -251,7 +252,7 @@
                                            jint audioFormat, jint buffSizeInBytes, jint memoryMode,
                                            jintArray jSession, jlong nativeAudioTrack,
                                            jboolean offload, jint encapsulationMode,
-                                           jobject tunerConfiguration) {
+                                           jobject tunerConfiguration, jstring opPackageName) {
     ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
           " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p",
           jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
@@ -337,7 +338,8 @@
         }
 
         // create the native AudioTrack object
-        lpTrack = new AudioTrack();
+        ScopedUtfChars opPackageNameStr(env, opPackageName);
+        lpTrack = new AudioTrack(opPackageNameStr.c_str());
 
         // read the AudioAttributes values
         auto paa = JNIAudioAttributeHelper::makeUnique();
@@ -371,23 +373,24 @@
         status_t status = NO_ERROR;
         switch (memoryMode) {
         case MODE_STREAM:
-            status = lpTrack->set(
-                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
-                    sampleRateInHertz,
-                    format,// word length, PCM
-                    nativeChannelMask,
-                    offload ? 0 : frameCount,
-                    offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,
-                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
-                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
-                    0,// shared mem
-                    true,// thread can call Java
-                    sessionId,// audio session ID
-                    offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
-                    offload ? &offloadInfo : NULL,
-                    -1, -1,                       // default uid, pid values
-                    paa.get());
-
+            status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed
+                                                        // in paa (last argument)
+                                  sampleRateInHertz,
+                                  format, // word length, PCM
+                                  nativeChannelMask, offload ? 0 : frameCount,
+                                  offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
+                                          : AUDIO_OUTPUT_FLAG_NONE,
+                                  audioCallback,
+                                  &(lpJniStorage->mCallbackData), // callback, callback data (user)
+                                  0,    // notificationFrames == 0 since not using EVENT_MORE_DATA
+                                        // to feed the AudioTrack
+                                  0,    // shared mem
+                                  true, // thread can call Java
+                                  sessionId, // audio session ID
+                                  offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
+                                          : AudioTrack::TRANSFER_SYNC,
+                                  offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values
+                                  paa.get());
             break;
 
         case MODE_STATIC:
@@ -398,22 +401,22 @@
                 goto native_init_failure;
             }
 
-            status = lpTrack->set(
-                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
-                    sampleRateInHertz,
-                    format,// word length, PCM
-                    nativeChannelMask,
-                    frameCount,
-                    AUDIO_OUTPUT_FLAG_NONE,
-                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
-                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
-                    lpJniStorage->mMemBase,// shared mem
-                    true,// thread can call Java
-                    sessionId,// audio session ID
-                    AudioTrack::TRANSFER_SHARED,
-                    NULL,                         // default offloadInfo
-                    -1, -1,                       // default uid, pid values
-                    paa.get());
+            status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed
+                                                        // in paa (last argument)
+                                  sampleRateInHertz,
+                                  format, // word length, PCM
+                                  nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE,
+                                  audioCallback,
+                                  &(lpJniStorage->mCallbackData), // callback, callback data (user)
+                                  0, // notificationFrames == 0 since not using EVENT_MORE_DATA
+                                     // to feed the AudioTrack
+                                  lpJniStorage->mMemBase, // shared mem
+                                  true,                   // thread can call Java
+                                  sessionId,              // audio session ID
+                                  AudioTrack::TRANSFER_SHARED,
+                                  NULL,   // default offloadInfo
+                                  -1, -1, // default uid, pid values
+                                  paa.get());
             break;
 
         default:
@@ -1428,7 +1431,8 @@
         {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
         {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
         {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
-        {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I",
+        {"native_setup",
+         "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;Ljava/lang/String;)I",
          (void *)android_media_AudioTrack_setup},
         {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
         {"native_release", "()V", (void *)android_media_AudioTrack_release},
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index cbcbe7f..c08363b 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -19,18 +19,16 @@
 
 // To make sure cpu_set_t is included from sched.h
 #define _GNU_SOURCE 1
-#include <android-base/properties.h>
-#include <android-base/unique_fd.h>
-#include <binder/ActivityManager.h>
+#include <utils/Log.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
 #include <meminfo/procmeminfo.h>
 #include <meminfo/sysmeminfo.h>
 #include <processgroup/processgroup.h>
 #include <processgroup/sched_policy.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
+#include <android-base/unique_fd.h>
 
 #include <algorithm>
 #include <array>
@@ -86,8 +84,6 @@
 static pthread_key_t gBgKey = -1;
 #endif
 
-static bool boot_completed = false;
-
 // For both of these, err should be in the errno range (positive), not a status_t (negative)
 static void signalExceptionForError(JNIEnv* env, int err, int tid) {
     switch (err) {
@@ -372,22 +368,6 @@
     }
 }
 
-void android_os_Process_enableFreezer(
-        JNIEnv *env, jobject clazz, jboolean enable)
-{
-    bool success = true;
-
-    if (enable) {
-        success = SetTaskProfiles(0, {"FreezerFrozen"}, true);
-    } else {
-        success = SetTaskProfiles(0, {"FreezerThawed"}, true);
-    }
-
-    if (!success) {
-        jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
-    }
-}
-
 jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
 {
     SchedPolicy sp;
@@ -600,20 +580,7 @@
     }
 #endif
 
-    SchedPolicy policy;
-    bool policy_changed = false;
-    int rc = 0, curr_pri = getpriority(PRIO_PROCESS, pid);
-
-    if (pri == curr_pri) {
-        return;
-    }
-
-    if (!boot_completed) {
-        boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
-    }
-
-    // Do not change sched policy cgroup after boot complete.
-    rc = androidSetThreadPriorityAndPolicy(pid, pri, !boot_completed);
+    int rc = androidSetThreadPriority(pid, pri);
     if (rc != 0) {
         if (rc == INVALID_OPERATION) {
             signalExceptionForPriorityError(env, errno, pid);
@@ -622,31 +589,6 @@
         }
     }
 
-    // Only use async approach after boot complete.
-    if (!boot_completed) {
-        return;
-    }
-
-    // Change to background sched policy for the thread if setting to low priority.
-    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
-        policy = SP_BACKGROUND;
-        policy_changed = true;
-        // Change to sched policy of the process if thread priority is raising from low priority.
-    } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
-        // If we cannot get sched policy of the process, use SP_FOREGROUND as default.
-        policy = SP_FOREGROUND;
-        get_sched_policy(getpid(), &policy);
-        policy_changed = true;
-    }
-
-    // Sched policy will only change in above 2 cases.
-    if (policy_changed) {
-        ActivityManager am;
-        if (!am.setSchedPolicyCgroup(pid, policy)) {
-            ALOGE("am.setThreadPriority failed: tid=%d priority=%d policy=%d", pid, pri, policy);
-        }
-    }
-
     //ALOGI("Setting priority of %" PRId32 ": %" PRId32 ", getpriority returns %d\n",
     //     pid, pri, getpriority(PRIO_PROCESS, pid));
 }
@@ -1424,7 +1366,6 @@
         {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
         {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet},
         {"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen},
-        {"enableFreezer", "(Z)V", (void*)android_os_Process_enableFreezer},
         {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
         {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory},
         {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V",
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index 586b26e..cbce38e 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -1,18 +1,18 @@
 /*
  * Copyright 2006, 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 
+ * 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 
+ *     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 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
  * limitations under the License.
-*/
+ */
 
 #include <android_runtime/AndroidRuntime.h>
 
@@ -47,9 +47,8 @@
 
 class NativeKeyCharacterMap {
 public:
-    NativeKeyCharacterMap(int32_t deviceId, const sp<KeyCharacterMap>& map) :
-        mDeviceId(deviceId), mMap(map) {
-    }
+    NativeKeyCharacterMap(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map)
+          : mDeviceId(deviceId), mMap(std::move(map)) {}
 
     ~NativeKeyCharacterMap() {
     }
@@ -58,26 +57,22 @@
         return mDeviceId;
     }
 
-    inline const sp<KeyCharacterMap>& getMap() const {
-        return mMap;
-    }
+    inline const std::shared_ptr<KeyCharacterMap> getMap() const { return mMap; }
 
 private:
     int32_t mDeviceId;
-    sp<KeyCharacterMap> mMap;
+    std::shared_ptr<KeyCharacterMap> mMap;
 };
 
-
 jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId,
-        const sp<KeyCharacterMap>& kcm) {
-    NativeKeyCharacterMap* map = new NativeKeyCharacterMap(deviceId,
-            kcm.get() ? kcm : KeyCharacterMap::empty());
-    if (!map) {
-        return NULL;
+                                            const std::shared_ptr<KeyCharacterMap> kcm) {
+    NativeKeyCharacterMap* nativeMap = new NativeKeyCharacterMap(deviceId, kcm);
+    if (!nativeMap) {
+        return nullptr;
     }
 
     return env->NewObject(gKeyCharacterMapClassInfo.clazz, gKeyCharacterMapClassInfo.ctor,
-            reinterpret_cast<jlong>(map));
+                          reinterpret_cast<jlong>(nativeMap));
 }
 
 static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
@@ -91,7 +86,7 @@
         return 0;
     }
 
-    sp<KeyCharacterMap> kcm = KeyCharacterMap::readFromParcel(parcel);
+    std::shared_ptr<KeyCharacterMap> kcm = KeyCharacterMap::readFromParcel(parcel);
     if (!kcm.get()) {
         return 0;
     }
@@ -102,6 +97,9 @@
 
 static void nativeWriteToParcel(JNIEnv* env, jobject clazz, jlong ptr, jobject parcelObj) {
     NativeKeyCharacterMap* map = reinterpret_cast<NativeKeyCharacterMap*>(ptr);
+    if (!map->getMap()) {
+        return;
+    }
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
     if (parcel) {
         parcel->writeInt32(map->getDeviceId());
@@ -150,9 +148,8 @@
         return 0;
     }
 
-    char16_t result = map->getMap()->getMatch(
-        keyCode, reinterpret_cast<char16_t*>(chars), size_t(numChars),
-        metaState);
+    char16_t result = map->getMap()->getMatch(keyCode, reinterpret_cast<char16_t*>(chars),
+                                              size_t(numChars), metaState);
 
     env->ReleasePrimitiveArrayCritical(charsArray, chars, JNI_ABORT);
     return result;
@@ -180,8 +177,7 @@
 
     Vector<KeyEvent> events;
     jobjectArray result = NULL;
-    if (map->getMap()->getEvents(map->getDeviceId(),
-                                 reinterpret_cast<char16_t*>(chars),
+    if (map->getMap()->getEvents(map->getDeviceId(), reinterpret_cast<char16_t*>(chars),
                                  size_t(numChars), events)) {
         result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
         if (result) {
diff --git a/core/jni/android_view_KeyCharacterMap.h b/core/jni/android_view_KeyCharacterMap.h
index e8465c2..be03353 100644
--- a/core/jni/android_view_KeyCharacterMap.h
+++ b/core/jni/android_view_KeyCharacterMap.h
@@ -25,7 +25,7 @@
 
 /* Creates a KeyCharacterMap object from the given information. */
 extern jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId,
-        const sp<KeyCharacterMap>& map);
+                                                   const std::shared_ptr<KeyCharacterMap> kcm);
 
 } // namespace android
 
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 54567e5..8177ec6 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -20,6 +20,7 @@
 
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
+#include <attestation/HmacKeyManager.h>
 #include <input/Input.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/ScopedUtfChars.h>
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 2e396f2..ce8b599 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -21,12 +21,13 @@
 #include <android/graphics/matrix.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
-#include <utils/Log.h>
+#include <attestation/HmacKeyManager.h>
 #include <input/Input.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
 #include "android_os_Parcel.h"
-#include "android_view_MotionEvent.h"
 #include "android_util_Binder.h"
+#include "android_view_MotionEvent.h"
 
 #include "core_jni_helpers.h"
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 85b4fe1..f2b4a1b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -30,6 +30,7 @@
 #include <android_runtime/android_hardware_HardwareBuffer.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_view_SurfaceSession.h>
+#include <gui/IScreenCaptureListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -188,6 +189,11 @@
 
 static struct {
     jclass clazz;
+    jmethodID onScreenCaptureComplete;
+} gScreenCaptureListenerClassInfo;
+
+static struct {
+    jclass clazz;
     jmethodID ctor;
     jfieldID defaultConfig;
     jfieldID primaryRefreshRateMin;
@@ -226,6 +232,54 @@
     }
 }
 
+class ScreenCaptureListenerWrapper : public BnScreenCaptureListener {
+public:
+    explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
+        env->GetJavaVM(&mVm);
+        screenCaptureListenerObject = env->NewGlobalRef(jobject);
+        LOG_ALWAYS_FATAL_IF(!screenCaptureListenerObject, "Failed to make global ref");
+    }
+
+    ~ScreenCaptureListenerWrapper() {
+        if (screenCaptureListenerObject) {
+            getenv()->DeleteGlobalRef(screenCaptureListenerObject);
+            screenCaptureListenerObject = nullptr;
+        }
+    }
+
+    status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) {
+        JNIEnv* env = getenv();
+        if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) {
+            env->CallVoidMethod(screenCaptureListenerObject,
+                                gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
+            return NO_ERROR;
+        }
+        jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+                env, captureResults.buffer->toAHardwareBuffer());
+        const jint namedColorSpace =
+                fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
+        jobject screenshotHardwareBuffer =
+                env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+                                            gScreenshotHardwareBufferClassInfo.builder,
+                                            jhardwareBuffer, namedColorSpace,
+                                            captureResults.capturedSecureLayers);
+        env->CallVoidMethod(screenCaptureListenerObject,
+                            gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
+                            screenshotHardwareBuffer);
+        return NO_ERROR;
+    }
+
+private:
+    jobject screenCaptureListenerObject;
+    JavaVM* mVm;
+
+    JNIEnv* getenv() {
+        JNIEnv* env;
+        mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+        return env;
+    }
+};
+
 // ----------------------------------------------------------------------------
 
 static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -327,36 +381,28 @@
     return captureArgs;
 }
 
-static jobject nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject) {
+static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
+                                 jobject screenCaptureListenerObject) {
     const DisplayCaptureArgs captureArgs =
             displayCaptureArgsFromObject(env, displayCaptureArgsObject);
 
     if (captureArgs.displayToken == NULL) {
-        return NULL;
+        return BAD_VALUE;
     }
 
-    ScreenCaptureResults captureResults;
-    status_t res = ScreenshotClient::captureDisplay(captureArgs, captureResults);
-    if (res != NO_ERROR) {
-        return NULL;
-    }
-
-    jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
-            env, captureResults.buffer->toAHardwareBuffer());
-    const jint namedColorSpace =
-            fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
-    return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
-                                       gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
-                                       namedColorSpace, captureResults.capturedSecureLayers);
+    sp<IScreenCaptureListener> captureListener =
+            new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
+    return ScreenshotClient::captureDisplay(captureArgs, captureListener);
 }
 
-static jobject nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject) {
+static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
+                                jobject screenCaptureListenerObject) {
     LayerCaptureArgs captureArgs;
     getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
     SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
             env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
     if (layer == nullptr) {
-        return nullptr;
+        return BAD_VALUE;
     }
 
     captureArgs.layerHandle = layer->getHandle();
@@ -380,19 +426,9 @@
         env->ReleaseLongArrayElements(excludeObjectArray, const_cast<jlong*>(objects), JNI_ABORT);
     }
 
-    ScreenCaptureResults captureResults;
-    status_t res = ScreenshotClient::captureLayers(captureArgs, captureResults);
-    if (res != NO_ERROR) {
-        return NULL;
-    }
-
-    jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
-            env, captureResults.buffer->toAHardwareBuffer());
-    const jint namedColorSpace =
-            fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
-    return env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
-                                       gScreenshotHardwareBufferClassInfo.builder, jhardwareBuffer,
-                                       namedColorSpace, captureResults.capturedSecureLayers);
+    sp<IScreenCaptureListener> captureListener =
+            new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
+    return ScreenshotClient::captureLayers(captureArgs, captureListener);
 }
 
 static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
@@ -1505,9 +1541,25 @@
     return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
 }
 
+static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                   jobject toTokenObj, jobject focusedTokenObj, jint displayId) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    if (toTokenObj == NULL) return;
+
+    sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
+    sp<IBinder> focusedToken;
+    if (focusedTokenObj != NULL) {
+        focusedToken = ibinderForJavaObject(env, focusedTokenObj);
+    }
+    transaction->setFocusedWindow(toToken, focusedToken, systemTime(SYSTEM_TIME_MONOTONIC),
+                                  displayId);
+}
+
 // ----------------------------------------------------------------------------
 
+// clang-format off
 static const JNINativeMethod sSurfaceControlMethods[] = {
+        // clang-format off
     {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J",
             (void*)nativeCreate },
     {"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
@@ -1649,12 +1701,10 @@
     {"nativeSetOverrideScalingMode", "(JJI)V",
             (void*)nativeSetOverrideScalingMode },
     {"nativeCaptureDisplay",
-            "(Landroid/view/SurfaceControl$DisplayCaptureArgs;)"
-            "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
+            "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
             (void*)nativeCaptureDisplay },
     {"nativeCaptureLayers",
-            "(Landroid/view/SurfaceControl$LayerCaptureArgs;)"
-            "Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;",
+            "(Landroid/view/SurfaceControl$LayerCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
             (void*)nativeCaptureLayers },
     {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
             (void*)nativeSetInputWindowInfo },
@@ -1686,8 +1736,13 @@
             (void*)nativeSetGlobalShadowSettings },
     {"nativeGetHandle", "(J)J",
             (void*)nativeGetHandle },
-    {"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint},
+    {"nativeSetFixedTransformHint", "(JJI)V",
+            (void*)nativeSetFixedTransformHint},
+    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Landroid/os/IBinder;I)V",
+            (void*)nativeSetFocusedWindow},
+        // clang-format on
 };
+// clang-format on
 
 int register_android_view_SurfaceControl(JNIEnv* env)
 {
@@ -1856,6 +1911,12 @@
     gLayerCaptureArgsClassInfo.childrenOnly =
             GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
 
+    jclass screenCaptureListenerClazz =
+            FindClassOrDie(env, "android/view/SurfaceControl$ScreenCaptureListener");
+    gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
+    gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
+            GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
+                             "(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V");
     return err;
 }
 
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 6212bcb..7fac615 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2748,4 +2748,7 @@
     // CATEGORY: SETTINGS
     // OS: R QPR
     ADAPTIVE_CONNECTIVITY_CATEGORY = 1850;
+
+    // OS: R QPR2
+    BLUETOOTH_PAIRING_RECEIVER = 1851;
 }
diff --git a/core/proto/android/providers/settings/config.proto b/core/proto/android/providers/settings/config.proto
index b0a70ef..d433c0e 100644
--- a/core/proto/android/providers/settings/config.proto
+++ b/core/proto/android/providers/settings/config.proto
@@ -31,11 +31,14 @@
   repeated SettingProto activity_manager_settings = 4;
   repeated SettingProto app_compat_settings = 5;
   repeated SettingProto autofill_settings = 6;
+  repeated SettingProto blobstore_settings = 23;
   repeated SettingProto connectivity_settings = 7;
   repeated SettingProto content_capture_settings = 8;
-  repeated SettingProto dex_boot_settings = 9;
+  repeated SettingProto device_idle_settings = 24;
+  reserved 9; // dex_boot_settings
   repeated SettingProto game_driver_settings = 10;
   repeated SettingProto input_native_boot_settings = 11;
+  repeated SettingProto job_scheduler_settings = 25;
   repeated SettingProto netd_native_settings = 12;
   repeated SettingProto privacy_settings = 13;
   repeated SettingProto rollback_boot_settings = 14;
@@ -47,7 +50,8 @@
   repeated SettingProto systemui_settings = 20;
   repeated SettingProto telephony_settings = 21;
   repeated SettingProto textclassifier_settings = 22;
-  repeated SettingProto blobstore_settings = 23;
+
+  // Next tag: 26
 
   message NamespaceProto {
     optional string namespace = 1;
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 9fccdaf..3ec14c0 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -292,7 +292,7 @@
         optional SettingProto name = 1;
         optional SettingProto provisioned = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto provisioning_mobile_data_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto idle_constants = 4;
+        reserved 4; // idle_constants
         optional SettingProto policy_constants = 5;
         optional SettingProto demo_mode = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index ff0ba8d..3a59a16 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -316,6 +316,7 @@
     optional bool front_of_task = 28;
     optional int32 proc_id = 29;
     optional bool translucent = 30;
+    optional bool pip_auto_enter_allowed = 31;
 }
 
 /* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57c1fcf..c9cecdd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3094,7 +3094,8 @@
         android:protectionLevel="signature" />
 
     <!-- Allows an application to be the status bar.  Currently used only by SystemUI.apk
-    @hide -->
+        @hide
+        @SystemApi -->
     <permission android:name="android.permission.STATUS_BAR_SERVICE"
         android:protectionLevel="signature" />
 
@@ -5495,6 +5496,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ProfcollectBGJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service
                 android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
                 android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 0e176b5..feef587 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1283,7 +1283,7 @@
     <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"nepoznata vrsta mreže"</string>
     <string name="accept" msgid="5447154347815825107">"Prihvati"</string>
     <string name="decline" msgid="6490507610282145874">"Odbijte"</string>
-    <string name="select_character" msgid="3352797107930786979">"Umetni karakter"</string>
+    <string name="select_character" msgid="3352797107930786979">"Umetni znak"</string>
     <string name="sms_control_title" msgid="4748684259903148341">"Slanje SMS poruka"</string>
     <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje veliki broj SMS poruka. Da li želite dozvoliti ovoj aplikaciji da nastavi slanje poruka?"</string>
     <string name="sms_control_yes" msgid="4858845109269524622">"Dozvoli"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 34e6efb..fcf9b83 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1261,7 +1261,7 @@
     <string name="volume_ringtone" msgid="134784084629229029">"Hlasitost vyzvánění"</string>
     <string name="volume_music" msgid="7727274216734955095">"Hlasitost médií"</string>
     <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"Přehrávání pomocí rozhraní Bluetooth"</string>
-    <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"Je nastaven tichý vyzváněcí tón"</string>
+    <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"Je nastaven tichý vyzvánění"</string>
     <string name="volume_call" msgid="7625321655265747433">"Hlasitost hovoru"</string>
     <string name="volume_bluetooth_call" msgid="2930204618610115061">"Hlasitost příchozích hovorů při připojení Bluetooth"</string>
     <string name="volume_alarm" msgid="4486241060751798448">"Hlasitost budíku"</string>
@@ -1272,10 +1272,10 @@
     <string name="volume_icon_description_incall" msgid="4491255105381227919">"Hlasitost hovoru"</string>
     <string name="volume_icon_description_media" msgid="4997633254078171233">"Hlasitost médií"</string>
     <string name="volume_icon_description_notification" msgid="579091344110747279">"Hlasitost oznámení"</string>
-    <string name="ringtone_default" msgid="9118299121288174597">"Výchozí vyzváněcí tón"</string>
+    <string name="ringtone_default" msgid="9118299121288174597">"Výchozí vyzvánění"</string>
     <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Výchozí (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
     <string name="ringtone_silent" msgid="397111123930141876">"Žádný"</string>
-    <string name="ringtone_picker_title" msgid="667342618626068253">"Vyzváněcí tóny"</string>
+    <string name="ringtone_picker_title" msgid="667342618626068253">"Vyzvánění"</string>
     <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Zvuky budíku"</string>
     <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Zvuky upozornění"</string>
     <string name="ringtone_unknown" msgid="5059495249862816475">"Neznámé"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 117fecb..0b7e396 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -657,7 +657,7 @@
     <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"observer netværksforhold"</string>
     <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Tillader, at en applikation observerer netværksforhold. Bør aldrig være nødvendigt for almindelige apps."</string>
     <string name="permlab_setInputCalibration" msgid="932069700285223434">"skift kalibrering for inputenheden"</string>
-    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Tillader, at appen ændrer kalibreringsparametrene for berøringsskærmen. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Tillader, at appen ændrer kalibreringsparametrene for touchskærmen. Dette bør aldrig være nødvendigt for almindelige apps."</string>
     <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"få adgang til DRM-certifikater"</string>
     <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Tillader, at en applikation provisionerer og anvender DRM-certifikater. Dette bør aldrig være nødvendigt for almindelige apps."</string>
     <string name="permlab_handoverStatus" msgid="7620438488137057281">"modtag status for Android Beam-overførsler"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index ecce980..c93dbbf 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -484,8 +484,8 @@
     <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Telefonoaren ordu-zona aldatzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_getAccounts" msgid="5304317160463582791">"bilatu gailuko kontuak"</string>
     <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Tabletak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Instalatuta dauzkazun aplikazioek sortutako kontuak har daitezke barnean."</string>
-    <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Android TV gailuak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Kontu horien artean, instalatuta dituzun aplikazioek sortutako kontuak egon litezke."</string>
-    <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Telefonoak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Instalatuta dituzun aplikazioek sortutako kontuak har daitezke barnean."</string>
+    <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Android TV gailuak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Kontu horien artean, instalatuta dauzkazun aplikazioek sortutako kontuak egon litezke."</string>
+    <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Telefonoak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Instalatuta dauzkazun aplikazioek sortutako kontuak har daitezke barnean."</string>
     <string name="permlab_accessNetworkState" msgid="2349126720783633918">"ikusi sareko konexioak"</string>
     <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Sareko konexioei buruzko informazioa ikusteko baimena ematen die aplikazioei; adibidez, zer sare dauden eta zeintzuk dauden konektatuta."</string>
     <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"izan sarerako sarbide osoa"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 0004ff1..9062435 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1900,8 +1900,8 @@
     <string name="profile_encrypted_message" msgid="1128512616293157802">"A feloldáshoz koppintson rá"</string>
     <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Csatlakoztatva a(z) <xliff:g id="PRODUCT_NAME">%1$s</xliff:g> eszközhöz"</string>
     <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Koppintson ide a fájlok megtekintéséhez"</string>
-    <string name="pin_target" msgid="8036028973110156895">"Rögzítés"</string>
-    <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítése"</string>
+    <string name="pin_target" msgid="8036028973110156895">"Kitűzés"</string>
+    <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> kitűzése"</string>
     <string name="unpin_target" msgid="3963318576590204447">"Feloldás"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> rögzítésének feloldása"</string>
     <string name="app_info" msgid="6113278084877079851">"Alkalmazásinformáció"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 264a8fc..4f66b6e 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1979,7 +1979,7 @@
     <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Չհաջողվեց վերականգնել դյուրանցումը, քանի որ հավելվածների ստորագրությունները տարբեր են"</string>
     <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Չհաջողվեց վերականգնել դյուրանցումը"</string>
     <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Դյուրանցումն անջատված է"</string>
-    <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ՀԵՌԱՑՆԵԼ"</string>
+    <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ԱՊԱՏԵՂԱԴՐԵԼ"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"ԲԱՑԵԼ"</string>
     <string name="harmful_app_warning_title" msgid="8794823880881113856">"Հայտնաբերվել է վնասաբեր հավելված"</string>
     <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f9e3e2f..f4fb83d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -2035,7 +2035,7 @@
     <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Aplikasi ini tidak diberi izin merekam, tetapi dapat merekam audio melalui perangkat USB ini."</string>
     <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Beranda"</string>
     <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Kembali"</string>
-    <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplikasi yang Baru Dipakai"</string>
+    <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplikasi Terbaru"</string>
     <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Notifikasi"</string>
     <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Setelan Cepat"</string>
     <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Dialog Daya"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index a65f80c..2c041dc 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1120,7 +1120,7 @@
     <string name="yes" msgid="9069828999585032361">"ОК"</string>
     <string name="no" msgid="5122037903299899715">"Цуцлах"</string>
     <string name="dialog_alert_title" msgid="651856561974090712">"Анхаар"</string>
-    <string name="loading" msgid="3138021523725055037">"Ачааллаж байна..."</string>
+    <string name="loading" msgid="3138021523725055037">"Ачаалж байна..."</string>
     <string name="capital_on" msgid="2770685323900821829">"Идэвхтэй"</string>
     <string name="capital_off" msgid="7443704171014626777">"Идэвхгүй"</string>
     <string name="checked" msgid="9179896827054513119">"тэмдэглэсэн"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index bb79c21..ee0362e 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -304,13 +304,13 @@
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
     <string name="permgrouplab_storage" msgid="1938416135375282333">"फाइल र मिडिया"</string>
-    <string name="permgroupdesc_storage" msgid="6351503740613026600">"तपाईंको यन्त्रमा तस्बिर, मिडिया, र फाइलहरूमाथि पहुँच गर्नुहोस्"</string>
+    <string name="permgroupdesc_storage" msgid="6351503740613026600">"तपाईंको यन्त्रमा फोटो, मिडिया, र फाइलहरूमाथि पहुँच गर्नुहोस्"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"माइक्रोफोन"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"अडियो रेकर्ड गर्नुहोस्"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"शारीरिक क्रियाकलाप"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"आफ्नो शारीरिक क्रियाकलापको डेटामाथि पहुँच राख्नु"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"क्यामेरा"</string>
-    <string name="permgroupdesc_camera" msgid="7585150538459320326">"तस्बिर खिच्नुका साथै भिडियो रेकर्ड गर्नुहोस्"</string>
+    <string name="permgroupdesc_camera" msgid="7585150538459320326">"फोटो खिच्नुका साथै भिडियो रेकर्ड गर्नुहोस्"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"कलका लगहरू"</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"फोन कलको लग पढ्नुहोस् र लेख्नुहोस्"</string>
     <string name="permgrouplab_phone" msgid="570318944091926620">"फोन"</string>
@@ -437,10 +437,10 @@
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"SIM लाई आदेश पठाउन एपलाई अनुमति दिन्छ। यो निकै खतरनाक हुन्छ।"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"शारीरिक गतिविधि पहिचान गर्नुहोस्‌"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"यो अनुप्रयोगले तपाईंको शारीरिक गतिविधिको पहिचान गर्न सक्छ।"</string>
-    <string name="permlab_camera" msgid="6320282492904119413">"तस्बिरहरू र भिडियोहरू लिनुहोस्।"</string>
-    <string name="permdesc_camera" msgid="1354600178048761499">"यस अनुप्रयोगले जुनसुकै समय क्यामेराको प्रयोग गरी तस्बिर खिच्न र भिडियो रेकर्ड गर्न सक्छ।"</string>
-    <string name="permlab_systemCamera" msgid="3642917457796210580">"एप वा सेवालाई तस्बिर र भिडियो खिच्न प्रणालीका क्यामेराहरूमाथि पहुँच राख्न दिनुहोस्"</string>
-    <string name="permdesc_systemCamera" msgid="5938360914419175986">"प्रणालीको यस विशेषाधिकार प्राप्त अनुप्रयोगले जुनसुकै बेला प्रणालीको क्यामेरा प्रयोग गरी तस्बिर खिच्न र भिडियो रेकर्ड गर्न सक्छ। अनुप्रयोगसँग पनि android.permission.CAMERA प्रयोग गर्ने अनुमति हुनु पर्छ"</string>
+    <string name="permlab_camera" msgid="6320282492904119413">"फोटोहरू र भिडियोहरू लिनुहोस्।"</string>
+    <string name="permdesc_camera" msgid="1354600178048761499">"यस अनुप्रयोगले जुनसुकै समय क्यामेराको प्रयोग गरी फोटो खिच्न र भिडियो रेकर्ड गर्न सक्छ।"</string>
+    <string name="permlab_systemCamera" msgid="3642917457796210580">"एप वा सेवालाई फोटो र भिडियो खिच्न प्रणालीका क्यामेराहरूमाथि पहुँच राख्न दिनुहोस्"</string>
+    <string name="permdesc_systemCamera" msgid="5938360914419175986">"प्रणालीको यस विशेषाधिकार प्राप्त अनुप्रयोगले जुनसुकै बेला प्रणालीको क्यामेरा प्रयोग गरी फोटो खिच्न र भिडियो रेकर्ड गर्न सक्छ। अनुप्रयोगसँग पनि android.permission.CAMERA प्रयोग गर्ने अनुमति हुनु पर्छ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"कुनै एप वा सेवालाई खोलिँदै वा बन्द गरिँदै गरेका क्यामेरा यन्त्रहरूका बारेमा कलब्याक प्राप्त गर्ने अनुमति दिनुहोस्।"</string>
     <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"कुनै क्यामेरा यन्त्र खोलिँदा (कुन अनुप्रयोगले खोलेको भन्ने बारेमा) वा बन्द गरिँदा यो अनुप्रयोगले कलब्याक प्राप्त गर्न सक्छ।"</string>
     <string name="permlab_vibrate" msgid="8596800035791962017">"कम्पन नियन्त्रण गर्नुहोस्"</string>
@@ -1347,7 +1347,7 @@
     <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> ले काम गरिरहेको छैन"</string>
     <string name="ext_media_new_notification_message" msgid="6095403121990786986">"सेटअप गर्न ट्याप गर्नुहोस्"</string>
     <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"तपाईंले यो यन्त्र पुनः फर्म्याट गर्नु पर्ने हुन सक्छ। यो यन्त्र हटाउन ट्याप गर्नुहोस्।"</string>
-    <string name="ext_media_ready_notification_message" msgid="777258143284919261">"तस्बिरहरू र मिडिया स्थानान्तरणका लागि"</string>
+    <string name="ext_media_ready_notification_message" msgid="777258143284919261">"फोटोहरू र मिडिया स्थानान्तरणका लागि"</string>
     <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"<xliff:g id="NAME">%s</xliff:g> मा समस्या देखियो"</string>
     <string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> ले काम गरिरहेको छैन"</string>
     <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"समस्या समाधान गर्न ट्याप गर्नुहोस्"</string>
@@ -1914,7 +1914,7 @@
     <string name="app_category_game" msgid="4534216074910244790">"खेलहरू"</string>
     <string name="app_category_audio" msgid="8296029904794676222">"सङ्गीत तथा अडियो"</string>
     <string name="app_category_video" msgid="2590183854839565814">"चलचित्र तथा भिडियो"</string>
-    <string name="app_category_image" msgid="7307840291864213007">"तस्बिर तथा छविहरू"</string>
+    <string name="app_category_image" msgid="7307840291864213007">"फोटो तथा छविहरू"</string>
     <string name="app_category_social" msgid="2278269325488344054">"सामाजिक तथा सञ्चार"</string>
     <string name="app_category_news" msgid="1172762719574964544">"समाचार तथा पत्रिकाहरू"</string>
     <string name="app_category_maps" msgid="6395725487922533156">"नक्सा तथा नेभिगेसन"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2b51170..20ef017f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2784,7 +2784,11 @@
              exactly one parameter of type View. For instance, if you specify
              <code>android:onClick="sayHello"</code>, you must declare a
              <code>public void sayHello(View v)</code> method of your context
-             (typically, your Activity). -->
+             (typically, your Activity).
+             {@deprecated View actually traverses the Context
+             hierarchy looking for the relevant method, which is fragile (an intermediate 
+             ContextWrapper adding a same-named method would change behavior) and restricts
+             bytecode optimizers such as R8. Instead, use View.setOnClickListener.}-->
         <attr name="onClick" format="string" />
 
         <!-- Defines over-scrolling behavior. This property is used only if the
@@ -7505,7 +7509,11 @@
         <attr name="enabled" />
 
         <!-- Name of a method on the Context used to inflate the menu that will be
-             called when the item is clicked. -->
+             called when the item is clicked.
+             {@deprecated Menu actually traverses the Context hierarchy looking for the 
+             relevant method, which is fragile (an intermediate ContextWrapper adding a
+             same-named method would change behavior) and restricts bytecode optimizers
+             such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} -->
         <attr name="onClick" />
 
         <!-- How this item should display in the Action Bar, if present. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5f2e4f9..550162a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2258,7 +2258,7 @@
 
     <!-- Amount of time in ms the user needs to press the relevant keys to trigger the
          screenshot chord -->
-    <integer name="config_screenshotChordKeyTimeout">500</integer>
+    <integer name="config_screenshotChordKeyTimeout">0</integer>
 
     <!-- Default width of a vertical scrollbar and height of a horizontal scrollbar.
          Takes effect only if the scrollbar drawables have no intrinsic size. -->
@@ -2783,6 +2783,7 @@
         <item>power</item>
         <item>restart</item>
         <item>logout</item>
+        <item>screenshot</item>
         <item>bugreport</item>
     </string-array>
 
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index eb69525..a5b7c61 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -16,9 +16,11 @@
 
 package android.view;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.ImeInsetsSourceConsumer.areEditorsSimilar;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -86,6 +88,7 @@
                     false,
                     new DisplayCutout(
                             Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
+                    TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
                     SOFT_INPUT_ADJUST_RESIZE, 0, 0);
             mImeConsumer = (ImeInsetsSourceConsumer) mController.getSourceConsumer(ITYPE_IME);
         });
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index de128ad..88e1f57 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
 import static android.view.InsetsController.ANIMATION_TYPE_NONE;
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
@@ -30,6 +31,7 @@
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -169,6 +171,7 @@
                     false,
                     new DisplayCutout(
                             Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
+                    TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
                     SOFT_INPUT_ADJUST_RESIZE, 0, 0);
             mController.onFrameChanged(new Rect(0, 0, 100, 100));
         });
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index c7d835c..4306e3e 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.InsetsState.ISIDE_BOTTOM;
 import static android.view.InsetsState.ISIDE_TOP;
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
@@ -30,9 +32,13 @@
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -78,7 +84,8 @@
             mState.getSource(ITYPE_IME).setVisible(true);
             SparseIntArray typeSideMap = new SparseIntArray();
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, typeSideMap);
+                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
+                    TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, typeSideMap);
             assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
             assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
             assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
@@ -97,7 +104,8 @@
             mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
             mState.getSource(ITYPE_IME).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
+                    TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
             assertEquals(100, insets.getStableInsetBottom());
             assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars()));
             assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -116,7 +124,8 @@
             mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
             mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+                    WINDOWING_MODE_UNDEFINED, null);
             assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
             assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
             assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -132,7 +141,8 @@
             mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
             mState.getSource(ITYPE_IME).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0,
+                    TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
             assertEquals(0, insets.getSystemWindowInsetBottom());
             assertEquals(100, insets.getInsets(ime()).bottom);
             assertTrue(insets.isVisible(ime()));
@@ -149,11 +159,11 @@
             mState.getSource(ITYPE_IME).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
                     false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
-                    SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
+                    SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
             assertEquals(100, insets.getSystemWindowInsetTop());
             insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
                     DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
-                    0 /* legacySystemUiFlags */, null);
+                    0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
             assertEquals(0, insets.getSystemWindowInsetTop());
         }
     }
@@ -166,15 +176,37 @@
             mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
                     false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN,
-                    SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
+                    SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
             assertEquals(0, insets.getSystemWindowInsetTop());
             insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
                     DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
-                    0 /* legacySystemUiFlags */, null);
+                    0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
             assertEquals(0, insets.getSystemWindowInsetTop());
         }
     }
 
+    @Test
+    public void testCalculateInsets_flagLayoutNoLimits() {
+        mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+        assertEquals(0, insets.getSystemWindowInsetTop());
+        insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_SYSTEM_ERROR, WINDOWING_MODE_UNDEFINED, null);
+        assertEquals(100, insets.getSystemWindowInsetTop());
+        insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_WALLPAPER, WINDOWING_MODE_UNDEFINED, null);
+        assertEquals(100, insets.getSystemWindowInsetTop());
+        insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_FREEFORM, null);
+        assertEquals(100, insets.getSystemWindowInsetTop());
+    }
+
 
     @Test
     public void testCalculateInsets_captionStatusBarOverlap() throws Exception {
@@ -213,7 +245,8 @@
             mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
             mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+                    WINDOWING_MODE_UNDEFINED, null);
             assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
             assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
             assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -229,7 +262,8 @@
             mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
             mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
             WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                    false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
+                    false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+                    WINDOWING_MODE_UNDEFINED, null);
             assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
             assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
             assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -244,7 +278,8 @@
         mState.getSource(ITYPE_IME).setVisible(true);
         mState.removeSource(ITYPE_IME);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
-                DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null);
+                DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION,
+                WINDOWING_MODE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
     }
 
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 5c16772..4cf6715 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -24,6 +24,7 @@
 import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -117,7 +118,15 @@
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
         ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
 
-        // A window which fits system bars must fit IME, unless its type is toast or system alert.
+        assertEquals(Type.systemBars(), attrs.getFitInsetsTypes());
+    }
+
+    @Test
+    public void adjustLayoutParamsForCompatibility_fitSystemBarsAndIme() {
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
+        attrs.softInputMode |= SOFT_INPUT_ADJUST_RESIZE;
+        ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
+
         assertEquals(Type.systemBars() | Type.ime(), attrs.getFitInsetsTypes());
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
new file mode 100644
index 0000000..ece5037
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.jank;
+
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.view.FrameMetrics;
+import android.view.View;
+import android.view.ViewAttachTestActivity;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
+import com.android.internal.jank.InteractionJankMonitor.Session;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+public class FrameTrackerTest {
+    private ViewAttachTestActivity mActivity;
+
+    @Rule
+    public ActivityTestRule<ViewAttachTestActivity> mRule =
+            new ActivityTestRule<>(ViewAttachTestActivity.class);
+
+    private FrameTracker mTracker;
+    private ThreadedRendererWrapper mRenderer;
+    private FrameMetricsWrapper mWrapper;
+
+    @Before
+    public void setup() {
+        // Prepare an activity for getting ThreadedRenderer later.
+        mActivity = mRule.getActivity();
+        View view = mActivity.getWindow().getDecorView();
+        assertThat(view.isAttachedToWindow()).isTrue();
+
+        Handler handler = mRule.getActivity().getMainThreadHandler();
+        mWrapper = Mockito.spy(new FrameMetricsWrapper());
+        // For simplicity - provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
+        when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
+                .then(unusedInvocation -> System.nanoTime());
+        mRenderer = Mockito.spy(new ThreadedRendererWrapper(view.getThreadedRenderer()));
+        doNothing().when(mRenderer).addObserver(any());
+        doNothing().when(mRenderer).removeObserver(any());
+
+        Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
+        mTracker = Mockito.spy(new FrameTracker(session, handler, mRenderer, mWrapper));
+        doNothing().when(mTracker).triggerPerfetto();
+    }
+
+    @Test
+    public void testIgnoresSecondBegin() {
+        // Observer should be only added once in continuous calls.
+        mTracker.begin();
+        mTracker.begin();
+        verify(mRenderer, only()).addObserver(any());
+    }
+
+    @Test
+    public void testOnlyFirstFrameOverThreshold() {
+        // Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
+        when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
+                .then(unusedInvocation -> System.nanoTime());
+
+        mTracker.begin();
+        verify(mRenderer, only()).addObserver(any());
+
+        // send first frame with a long duration - should not be taken into account
+        setupFirstFrameMockWithDuration(100);
+        mTracker.onFrameMetricsAvailable(0);
+
+        // send another frame with a short duration - should not be considered janky
+        setupOtherFrameMockWithDuration(5);
+        mTracker.onFrameMetricsAvailable(0);
+
+        // end the trace session, the last janky frame is after the end() so is discarded.
+        mTracker.end();
+        setupOtherFrameMockWithDuration(500);
+        mTracker.onFrameMetricsAvailable(0);
+
+        verify(mRenderer).removeObserver(any());
+        verify(mTracker, never()).triggerPerfetto();
+    }
+
+    @Test
+    public void testOtherFrameOverThreshold() {
+        mTracker.begin();
+        verify(mRenderer, only()).addObserver(any());
+
+        // send first frame - not janky
+        setupFirstFrameMockWithDuration(4);
+        mTracker.onFrameMetricsAvailable(0);
+
+        // send another frame - should be considered janky
+        setupOtherFrameMockWithDuration(40);
+        mTracker.onFrameMetricsAvailable(0);
+
+        // end the trace session
+        mTracker.end();
+        setupOtherFrameMockWithDuration(5);
+        mTracker.onFrameMetricsAvailable(0);
+
+        verify(mRenderer).removeObserver(any());
+
+        // We detected a janky frame - trigger Perfetto
+        verify(mTracker).triggerPerfetto();
+    }
+
+    @Test
+    public void testLastFrameOverThresholdBeforeEnd() {
+        mTracker.begin();
+        verify(mRenderer, only()).addObserver(any());
+
+        // send first frame - not janky
+        setupFirstFrameMockWithDuration(4);
+        mTracker.onFrameMetricsAvailable(0);
+
+        // send another frame - not janky
+        setupOtherFrameMockWithDuration(4);
+        mTracker.onFrameMetricsAvailable(0);
+
+        // end the trace session, simulate one more valid callback came after the end call.
+        when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
+                .thenReturn(System.nanoTime());
+        setupOtherFrameMockWithDuration(50);
+        mTracker.end();
+        mTracker.onFrameMetricsAvailable(0);
+
+        // One more callback with VSYNC after the end() timestamp.
+        when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
+                .thenReturn(System.nanoTime());
+        setupOtherFrameMockWithDuration(5);
+        mTracker.onFrameMetricsAvailable(0);
+
+        verify(mRenderer).removeObserver(any());
+
+        // We detected a janky frame - trigger Perfetto
+        verify(mTracker).triggerPerfetto();
+    }
+
+    private void setupFirstFrameMockWithDuration(long durationMillis) {
+        doReturn(1L).when(mWrapper).getMetric(FrameMetrics.FIRST_DRAW_FRAME);
+        doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
+                .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
+    }
+
+    private void setupOtherFrameMockWithDuration(long durationMillis) {
+        doReturn(0L).when(mWrapper).getMetric(FrameMetrics.FIRST_DRAW_FRAME);
+        doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
+                .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
new file mode 100644
index 0000000..5c0b0c9
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.jank;
+
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.View;
+import android.view.ViewAttachTestActivity;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
+import com.android.internal.jank.InteractionJankMonitor.Session;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.testng.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@SmallTest
+public class InteractionJankMonitorTest {
+    private ViewAttachTestActivity mActivity;
+    private View mView;
+    private FrameTracker mTracker;
+
+    @Rule
+    public ActivityTestRule<ViewAttachTestActivity> mRule =
+            new ActivityTestRule<>(ViewAttachTestActivity.class);
+
+    @Before
+    public void setup() {
+        // Prepare an activity for getting ThreadedRenderer later.
+        mActivity = mRule.getActivity();
+        mView = mActivity.getWindow().getDecorView();
+        assertThat(mView.isAttachedToWindow()).isTrue();
+
+        InteractionJankMonitor.reset();
+
+        // Prepare a FrameTracker to inject.
+        Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
+        FrameMetricsWrapper wrapper = Mockito.spy(new FrameTracker.FrameMetricsWrapper());
+        ThreadedRendererWrapper renderer =
+                Mockito.spy(new ThreadedRendererWrapper(mView.getThreadedRenderer()));
+        Handler handler = mActivity.getMainThreadHandler();
+        mTracker = Mockito.spy(new FrameTracker(session, handler, renderer, wrapper));
+    }
+
+    @Test
+    public void testBeginEnd() {
+        // Should throw exception if the view is not attached.
+        Assert.assertThrows(IllegalStateException.class,
+                () -> InteractionJankMonitor.init(new View(mActivity)));
+
+        // Verify we init InteractionJankMonitor correctly.
+        Map<String, FrameTracker> map = new HashMap<>();
+        HandlerThread worker = Mockito.spy(new HandlerThread("Aot-test"));
+        doNothing().when(worker).start();
+        InteractionJankMonitor.init(mView, mView.getThreadedRenderer(), map, worker);
+        verify(worker).start();
+
+        // Simulate a trace session and see if begin / end are invoked.
+        Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
+        assertThat(map.get(session.getName())).isNull();
+        InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE, mTracker);
+        verify(mTracker).begin();
+        assertThat(map.get(session.getName())).isEqualTo(mTracker);
+        InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE);
+        verify(mTracker).end();
+        assertThat(map.get(session.getName())).isNull();
+    }
+
+    @Test
+    public void testCheckInitState() {
+        // Should throw exception if invoking begin / end without init invocation.
+        Assert.assertThrows(IllegalStateException.class,
+                () -> InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE));
+        Assert.assertThrows(IllegalStateException.class,
+                () -> InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE));
+
+        // Everything should be fine if invoking init first.
+        boolean thrown = false;
+        try {
+            InteractionJankMonitor.init(mActivity.getWindow().getDecorView());
+            InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE);
+            InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE);
+        } catch (Exception ex) {
+            thrown = true;
+        } finally {
+            assertThat(thrown).isFalse();
+        }
+    }
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
index 02870a5..7f4e9ad 100644
--- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
@@ -20,6 +20,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
 import android.app.EmptyActivity;
@@ -41,6 +44,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 /**
  * Tests {@link DecorContext}.
@@ -63,13 +67,18 @@
 
     @Test
     public void testDecorContextWithDefaultDisplay() {
+        final Context baseContext = Mockito.spy(mContext.getApplicationContext());
         Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(), DEFAULT_DISPLAY,
                 new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
         final Context defaultDisplayContext = mContext.createDisplayContext(defaultDisplay);
         final PhoneWindow window = new PhoneWindow(defaultDisplayContext);
-        DecorContext context = new DecorContext(mContext.getApplicationContext(), window);
+        DecorContext context = new DecorContext(baseContext, window);
 
         assertDecorContextDisplay(DEFAULT_DISPLAY, context);
+
+        // TODO(b/166174272): Creating a display context for the default display will result
+        // in additional resource creation.
+        verify(baseContext, never()).createDisplayContext(any());
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
index 359bd5e..4f0becc 100644
--- a/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/HexDumpTest.java
@@ -16,8 +16,16 @@
 
 package com.android.internal.util;
 
+import static com.android.internal.util.HexDump.hexStringToByteArray;
+import static com.android.internal.util.HexDump.toHexString;
+
 import junit.framework.TestCase;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Random;
+
 public final class HexDumpTest extends TestCase {
     public void testBytesToHexString() {
         assertEquals("abcdef", HexDump.toHexString(
@@ -25,7 +33,142 @@
         assertEquals("ABCDEF", HexDump.toHexString(
                 new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true));
     }
-    public void testNullArray() {
-        assertEquals("(null)", HexDump.toHexString(null));
+
+    public void testNullByteArray() {
+        assertThrows(
+                NullPointerException.class,
+                () -> HexDump.toHexString(null));
     }
+
+    public void testBytesToHexString_allByteValues() {
+        byte[] bytes = new byte[256];
+        for (int i = 0; i < bytes.length; i++) {
+            bytes[i] = (byte) (i % 256);
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (char firstChar : "0123456789ABCDEF".toCharArray()) {
+            for (char secondChar : "0123456789ABCDEF".toCharArray()) {
+                sb.append(firstChar).append(secondChar);
+            }
+        }
+        String expected = sb.toString();
+
+        assertEquals(expected, HexDump.toHexString(bytes));
+    }
+
+    public void testRoundTrip_fromBytes() {
+        Random deterministicRandom = new Random(31337); // arbitrary but deterministic
+        for (int length = 0; length < 100; length++) {
+            byte[] bytes = new byte[length];
+            deterministicRandom.nextBytes(bytes);
+            byte[] reconstruction = hexStringToByteArray(toHexString(bytes));
+
+            assertBytesEqual(bytes, reconstruction);
+        }
+    }
+
+    public void testRoundTrip_fromString() {
+        String hexString = "0123456789ABCDEF72f9a3438934c378d34f32a8b932";
+        for (int length = 0; length < hexString.length(); length += 2) {
+            String original = hexString.substring(0, length);
+            String reconstruction = toHexString(hexStringToByteArray(original));
+            assertEquals(original.toUpperCase(), reconstruction);
+        }
+    }
+
+    public void testToHexString_offsetLength() {
+        byte[] bytes = new byte[32];
+        for (int i = 0; i < 16; i++) {
+            bytes[i] = (byte) i;
+            bytes[16 + i] = (byte) (16 * i);
+        }
+        String expected = "000102030405060708090A0B0C0D0E0F00102030405060708090A0B0C0D0E0F0";
+        for (int offset = 0; offset < bytes.length; offset++) {
+            for (int len = 0; len < (bytes.length - offset); len++) {
+
+                byte[] subBytes = new byte[len];
+                System.arraycopy(bytes, offset, subBytes, 0, len);
+
+                String actual = toHexString(bytes, offset, len);
+                assertEquals(expected.substring(2 * offset, 2 * offset + 2 * len), actual);
+                assertEquals(toHexString(subBytes), actual);
+            }
+        }
+    }
+
+    public void testToHexString_case() {
+        byte[] bytes = new byte[32];
+        for (int i = 0; i < 16; i++) {
+            bytes[i] = (byte) i;
+            bytes[16 + i] = (byte) (16 * i);
+        }
+
+        String expected = "000102030405060708090A0B0C0D0E0F00102030405060708090A0B0C0D0E0F0";
+
+        assertEquals(expected.toUpperCase(), toHexString(bytes, true));
+        assertEquals(expected.toLowerCase(), toHexString(bytes, false));
+
+        // default is uppercase
+        assertEquals(expected.toUpperCase(), toHexString(bytes));
+    }
+
+    public void testHexStringToByteArray_empty() {
+        assertBytesEqual(new byte[0], HexDump.hexStringToByteArray(""));
+    }
+
+    public void testHexStringToByteArray_null() {
+        assertThrows(
+                NullPointerException.class,
+                () -> HexDump.hexStringToByteArray((String) null));
+    }
+
+    public void testHexStringToByteArray_invalidCharacters() {
+        // IllegalArgumentException would probably have been better than RuntimeException, but it
+        // might be too late to change now.
+        assertThrows(
+                RuntimeException.class,
+                () -> HexDump.hexStringToByteArray("GG"));
+        assertThrows(
+                RuntimeException.class,
+                () -> HexDump.hexStringToByteArray("\0\0"));
+        assertThrows(
+                RuntimeException.class,
+                () -> HexDump.hexStringToByteArray("abcdefgh"));
+    }
+
+    public void testHexStringToByteArray_oddLength() {
+        // IllegalArgumentException would probably have been better than
+        // StringIndexOutOfBoundsException, but it might be too late to change now.
+        assertThrows(
+                StringIndexOutOfBoundsException.class,
+                () -> HexDump.hexStringToByteArray("A"));
+        assertThrows(
+                StringIndexOutOfBoundsException.class,
+                () -> HexDump.hexStringToByteArray("123"));
+        assertThrows(
+                StringIndexOutOfBoundsException.class,
+                () -> HexDump.hexStringToByteArray("ABCDE"));
+    }
+
+    private static void assertBytesEqual(byte[] expected, byte[] actual) {
+        if (!Arrays.equals(expected, actual)) {
+            fail("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual));
+        }
+    }
+
+    private static void assertThrows(Class<? extends RuntimeException> clazz, Runnable runnable) {
+        try {
+            runnable.run();
+        } catch (RuntimeException exception) {
+            assertEquals(toStrackTrace(exception), clazz, exception.getClass());
+        }
+    }
+
+    private static String toStrackTrace(Throwable throwable) {
+        StringWriter stringWriter = new StringWriter();
+        throwable.printStackTrace(new PrintWriter(stringWriter));
+        return stringWriter.toString();
+    }
+
 }
diff --git a/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java
index 63a68e9..ab13fd7d 100644
--- a/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java
@@ -21,12 +21,11 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
-import static androidx.test.InstrumentationRegistry.getContext;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.content.Context;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -40,10 +39,12 @@
 import android.widget.TextView;
 
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.util.Random;
@@ -67,28 +68,27 @@
 
     private Random mRandom;
 
-    private static float sDensity;
-
-    @BeforeClass
-    public static void setUpClass() {
-        sDensity = getContext().getResources().getDisplayMetrics().density;
-    }
+    private Context mContext;
+    private float mDensity;
 
     @Before
     @UiThreadTest
     public void setUp() {
-        mRandom = new Random();
-        mParent = new FrameLayout(getContext());
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mDensity = mContext.getResources().getDisplayMetrics().density;
 
-        mTarget = new ScrollView(getContext());
+        mRandom = new Random();
+        mParent = new FrameLayout(mContext);
+
+        mTarget = new ScrollView(mContext);
         mParent.addView(mTarget, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 
-        mContent = new LinearLayout(getContext());
+        mContent = new LinearLayout(mContext);
         mContent.setOrientation(LinearLayout.VERTICAL);
         mTarget.addView(mContent, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
 
         for (int i = 0; i < CHILD_VIEWS; i++) {
-            TextView view = new TextView(getContext());
+            TextView view = new TextView(mContext);
             view.setText("Child #" + i);
             view.setTextColor(Color.WHITE);
             view.setTextSize(30f);
@@ -99,7 +99,7 @@
 
         // Window -> Parent -> Target -> Content
 
-        mWm = getContext().getSystemService(WindowManager.class);
+        mWm = mContext.getSystemService(WindowManager.class);
 
         // Setup the window that we are going to use
         mWindowLayoutParams = new WindowManager.LayoutParams(WINDOW_WIDTH, WINDOW_HEIGHT,
@@ -123,6 +123,190 @@
         svc.onPrepareForStart(mTarget, scrollBounds);
     }
 
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromTop() {
+        final int startScrollY = assertScrollToY(mTarget, 0);
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        assertTrue(scrollBounds.height() > CAPTURE_HEIGHT);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = svc.onScrollRequested(mTarget,
+                scrollBounds, request);
+
+        // The result is an empty rectangle and no scrolling, since it
+        // is not possible to physically scroll further up to make the
+        // requested area visible at all (it doesn't exist).
+        assertEmpty(scrollResult.availableArea);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromTop() {
+        final int startScrollY = assertScrollToY(mTarget, 0);
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        assertTrue(scrollBounds.height() > CAPTURE_HEIGHT);
+
+        // Capture between y = +1200 to +1500 pixels BELOW current top
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+        assertRectEquals(request, scrollResult.requestedArea);
+        assertRectEquals(request, scrollResult.availableArea);
+        assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+        assertEquals(CAPTURE_HEIGHT, scrollResult.scrollDelta);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromMiddle() {
+        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT);
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+        assertRectEquals(request, scrollResult.requestedArea);
+        assertRectEquals(request, scrollResult.availableArea);
+        assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+        assertEquals(-CAPTURE_HEIGHT, scrollResult.scrollDelta);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromMiddle() {
+        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT);
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+        assertRectEquals(request, scrollResult.requestedArea);
+        assertRectEquals(request, scrollResult.availableArea);
+        assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+        assertEquals(CAPTURE_HEIGHT, scrollResult.scrollDelta);
+
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromBottom() {
+        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT * 2);
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+        assertRectEquals(request, scrollResult.requestedArea);
+        assertRectEquals(request, scrollResult.availableArea);
+        assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
+        assertEquals(-CAPTURE_HEIGHT, scrollResult.scrollDelta);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromBottom() {
+        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT * 2);
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+        assertRectEquals(request, scrollResult.requestedArea);
+
+        // The result is an empty rectangle and no scrolling, since it
+        // is not possible to physically scroll further down to make the
+        // requested area visible at all (it doesn't exist).
+        assertEmpty(scrollResult.availableArea);
+        assertEquals(0, scrollResult.scrollDelta);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_offTopEdge() {
+        final int startScrollY = assertScrollToY(mTarget, 0);
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        // Create a request which lands halfway off the top of the content
+        //from -1500 to -900, (starting at 1200 = -300 to +300 within the content)
+        int top = 0;
+        Rect request = new Rect(
+                0, top - (CAPTURE_HEIGHT / 2),
+                scrollBounds.width(), top + (CAPTURE_HEIGHT / 2));
+
+        ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request);
+        assertRectEquals(request, scrollResult.requestedArea);
+
+        ScrollResult result = svc.onScrollRequested(mTarget, scrollBounds, request);
+        // The result is a partial result
+        Rect expectedResult = new Rect(request);
+        expectedResult.top += 300; // top half clipped
+        assertRectEquals(expectedResult, result.availableArea);
+        assertRequestedRectPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
+        assertEquals(0, scrollResult.scrollDelta);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_offBottomEdge() {
+        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT * 2); // 2400
+
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
+        svc.onPrepareForStart(mTarget, scrollBounds);
+
+        // Create a request which lands halfway off the bottom of the content
+        //from 600 to to 1200, (starting at 2400 = 3000 to  3600 within the content)
+
+        int bottom = WINDOW_HEIGHT;
+        Rect request = new Rect(
+                0, bottom - (CAPTURE_HEIGHT / 2),
+                scrollBounds.width(), bottom + (CAPTURE_HEIGHT / 2));
+
+        ScrollResult result = svc.onScrollRequested(mTarget, scrollBounds, request);
+
+        Rect expectedResult = new Rect(request);
+        expectedResult.bottom -= 300; // bottom half clipped
+        assertRectEquals(expectedResult, result.availableArea);
+        assertRequestedRectPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
+        assertEquals(0, result.scrollDelta);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onPrepareForEnd() {
+        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
+        svc.onPrepareForEnd(mTarget);
+    }
+
+
     static void assertEmpty(Rect r) {
         if (r != null && !r.isEmpty()) {
             fail("Not true that " + r + " is empty");
@@ -155,8 +339,7 @@
         return scrollY;
     }
 
-
-    static void assertCapturedAreaCompletelyVisible(int startScrollY, Rect requestRect,
+    static void assertRequestedRectCompletelyVisible(int startScrollY, Rect requestRect,
             Rect localVisibleNow) {
         Rect captured = new Rect(localVisibleNow);
         captured.offset(0, -startScrollY); // make relative
@@ -165,7 +348,7 @@
             fail("Not true that all of " + requestRect + " is contained by " + captured);
         }
     }
-    static void assertCapturedAreaPartiallyVisible(int startScrollY, Rect requestRect,
+    static void assertRequestedRectPartiallyVisible(int startScrollY, Rect requestRect,
             Rect localVisibleNow) {
         Rect captured = new Rect(localVisibleNow);
         captured.offset(0, -startScrollY); // make relative
@@ -174,179 +357,4 @@
             fail("Not true that any of " + requestRect + " intersects " + captured);
         }
     }
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_up_fromTop() {
-        final int startScrollY = assertScrollToY(mTarget, 0);
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        assertTrue(scrollBounds.height() > CAPTURE_HEIGHT);
-
-        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-
-        // The result is an empty rectangle and no scrolling, since it
-        // is not possible to physically scroll further up to make the
-        // requested area visible at all (it doesn't exist).
-        assertEmpty(result);
-    }
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_down_fromTop() {
-        final int startScrollY = assertScrollToY(mTarget, 0);
-
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        assertTrue(scrollBounds.height() > CAPTURE_HEIGHT);
-
-        // Capture between y = +1200 to +1500 pixels BELOW current top
-        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
-                WINDOW_HEIGHT + CAPTURE_HEIGHT);
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-        assertRectEquals(request, result);
-
-        assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
-    }
-
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_up_fromMiddle() {
-        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT);
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
-
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-
-        assertRectEquals(request, result);
-
-        assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
-    }
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_down_fromMiddle() {
-        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT);
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
-                WINDOW_HEIGHT + CAPTURE_HEIGHT);
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-        assertRectEquals(request, result);
-
-        assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
-    }
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_up_fromBottom() {
-        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT * 2);
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-        assertRectEquals(request, result);
-
-        assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent));
-    }
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_down_fromBottom() {
-        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT * 2);
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
-                WINDOW_HEIGHT + CAPTURE_HEIGHT);
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-
-        // The result is an empty rectangle and no scrolling, since it
-        // is not possible to physically scroll further down to make the
-        // requested area visible at all (it doesn't exist).
-        assertEmpty(result);
-    }
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_offTopEdge() {
-        final int startScrollY = assertScrollToY(mTarget, 0);
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        // Create a request which lands halfway off the top of the content
-        //from -1500 to -900, (starting at 1200 = -300 to +300 within the content)
-        int top = 0;
-        Rect request = new Rect(
-                0, top - (CAPTURE_HEIGHT / 2),
-                scrollBounds.width(), top + (CAPTURE_HEIGHT / 2));
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-        // The result is a partial result
-        Rect expectedResult = new Rect(request);
-        expectedResult.top += 300; // top half clipped
-        assertRectEquals(expectedResult, result);
-        assertCapturedAreaPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
-    }
-
-    @Test
-    @UiThreadTest
-    public void onScrollRequested_offBottomEdge() {
-        final int startScrollY = assertScrollToY(mTarget, WINDOW_HEIGHT * 2); // 2400
-
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        Rect scrollBounds = svc.onComputeScrollBounds(mTarget);
-        svc.onPrepareForStart(mTarget, scrollBounds);
-
-        // Create a request which lands halfway off the bottom of the content
-        //from 600 to to 1200, (starting at 2400 = 3000 to  3600 within the content)
-
-        int bottom = WINDOW_HEIGHT;
-        Rect request = new Rect(
-                0, bottom - (CAPTURE_HEIGHT / 2),
-                scrollBounds.width(), bottom + (CAPTURE_HEIGHT / 2));
-
-        Rect result = svc.onScrollRequested(mTarget, scrollBounds, request);
-
-        Rect expectedResult = new Rect(request);
-        expectedResult.bottom -= 300; // bottom half clipped
-        assertRectEquals(expectedResult, result);
-        assertCapturedAreaPartiallyVisible(startScrollY, request, getVisibleRect(mContent));
-
-    }
-
-    @Test
-    @UiThreadTest
-    public void onPrepareForEnd() {
-        ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper();
-        svc.onPrepareForEnd(mTarget);
-    }
 }
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 1cdc75a..9d95de7e 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -23,6 +23,8 @@
 import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -30,7 +32,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
@@ -50,6 +56,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.testing.PollingCheck;
 import android.view.WindowManagerGlobal;
 
 import androidx.test.annotation.UiThreadTest;
@@ -63,6 +70,8 @@
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Test for verifying {@link android.app.ActivityThread} class.
  *
@@ -76,6 +85,7 @@
 @MediumTest
 @Presubmit
 public class ActivityThreadClientTest {
+    private static final long WAIT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
 
     @Test
     @UiThreadTest
@@ -152,6 +162,63 @@
         }
     }
 
+    @Test
+    public void testLifecycleOfRelaunch() throws Exception {
+        try (ClientMockSession clientSession = new ClientMockSession()) {
+            ActivityThread activityThread = clientSession.mockThread();
+            ActivityClientRecord r = clientSession.stubActivityRecord();
+            final TestActivity[] activity = new TestActivity[1];
+
+            // Verify for ON_CREATE state. Activity should not be relaunched.
+            getInstrumentation().runOnMainSync(() -> {
+                activity[0] = (TestActivity) clientSession.launchActivity(r);
+            });
+            recreateAndVerifyNoRelaunch(activityThread, activity[0]);
+
+            // Verify for ON_START state. Activity should be relaunched.
+            getInstrumentation().runOnMainSync(() -> clientSession.startActivity(r));
+            recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_START);
+
+            // Verify for ON_RESUME state. Activity should be relaunched.
+            getInstrumentation().runOnMainSync(() -> clientSession.resumeActivity(r));
+            recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_RESUME);
+
+            // Verify for ON_PAUSE state. Activity should be relaunched.
+            getInstrumentation().runOnMainSync(() -> clientSession.pauseActivity(r));
+            recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_PAUSE);
+
+            // Verify for ON_STOP state. Activity should be relaunched.
+            getInstrumentation().runOnMainSync(() -> clientSession.stopActivity(r));
+            recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_STOP);
+
+            // Verify for ON_DESTROY state. Activity should not be relaunched.
+            getInstrumentation().runOnMainSync(() -> clientSession.destroyActivity(r));
+            recreateAndVerifyNoRelaunch(activityThread, activity[0]);
+        }
+    }
+
+    private void recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity) {
+        clearInvocations(activityThread);
+        getInstrumentation().runOnMainSync(() -> activity.recreate());
+
+        verify(activityThread, after(WAIT_TIMEOUT_MS).never())
+                .handleRelaunchActivity(any(), any());
+    }
+
+    private void recreateAndVerifyRelaunched(ActivityThread activityThread, TestActivity activity,
+            ActivityClientRecord r, int expectedState) throws Exception {
+        clearInvocations(activityThread);
+        getInstrumentation().runOnMainSync(() -> activity.recreate());
+
+        verify(activityThread, timeout(WAIT_TIMEOUT_MS)).handleRelaunchActivity(any(), any());
+
+        // Wait for the relaunch to complete.
+        PollingCheck.check("Waiting for the expected state " + expectedState + " timeout",
+                WAIT_TIMEOUT_MS,
+                () -> expectedState == r.getLifecycleState());
+        assertEquals(expectedState, r.getLifecycleState());
+    }
+
     private class ClientMockSession implements AutoCloseable {
         private MockitoSession mMockSession;
         private ActivityThread mThread;
@@ -200,6 +267,11 @@
                     false /* getNonConfigInstance */, "test");
         }
 
+        private ActivityThread mockThread() {
+            spyOn(mThread);
+            return mThread;
+        }
+
         private ActivityClientRecord stubActivityRecord() {
             ComponentName component = new ComponentName(
                     InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class);
diff --git a/core/tests/powertests/PowerStatsLoadTests/Android.bp b/core/tests/powertests/PowerStatsLoadTests/Android.bp
new file mode 100644
index 0000000..66c91ad
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/Android.bp
@@ -0,0 +1,13 @@
+android_test {
+    name: "PowerStatsLoadTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "compatibility-device-util-axt",
+        "junit",
+    ],
+    libs: ["android.test.runner"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
new file mode 100644
index 0000000..b1c2a63
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.core.powerstatsloadtests">
+
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.BATTERY_STATS"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.core.powerstatsloadtests"
+        android:label="Power Stats Load Tests" />
+
+    <queries>
+        <!-- The load test resolves http://... intents. Let it do so. -->
+        <package android:name="com.android.chrome"/>
+    </queries>
+</manifest>
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java
new file mode 100644
index 0000000..ca29426
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/ConnectivitySetupRule.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ConnectivitySetupRule implements TestRule {
+
+    private final boolean mWifiEnabled;
+    private final ConnectivityManager mConnectivityManager;
+    private final WifiManager mWifiManager;
+    private boolean mInitialWifiState;
+
+    public ConnectivitySetupRule(boolean wifiEnabled) {
+        mWifiEnabled = wifiEnabled;
+
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Context context = instrumentation.getContext();
+        mConnectivityManager = context.getSystemService(ConnectivityManager.class);
+        mWifiManager = context.getSystemService(WifiManager.class);
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    mInitialWifiState = isWiFiConnected();
+                    setWiFiState(mWifiEnabled);
+                    base.evaluate();
+                } finally {
+                    setWiFiState(mInitialWifiState);
+                }
+            }
+        };
+    }
+
+    private void setWiFiState(final boolean enable) throws InterruptedException {
+        boolean wiFiConnected = isWiFiConnected();
+        if (enable == wiFiConnected) {
+            return;
+        }
+
+        NetworkTracker tracker = new NetworkTracker(!mWifiEnabled);
+        mConnectivityManager.registerNetworkCallback(
+                new NetworkRequest.Builder()
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED).build(),
+                tracker);
+
+        if (enable) {
+            SystemUtil.runShellCommand("svc wifi enable");
+            //noinspection deprecation
+            SystemUtil.runWithShellPermissionIdentity(mWifiManager::reconnect,
+                    android.Manifest.permission.NETWORK_SETTINGS);
+        } else {
+            SystemUtil.runShellCommand("svc wifi disable");
+        }
+
+        tracker.waitForExpectedState();
+
+        assertEquals("Wifi must be " + (enable ? "connected to" : "disconnected from")
+                + " an access point for this test.", enable, isWiFiConnected());
+
+        mConnectivityManager.unregisterNetworkCallback(tracker);
+    }
+
+    private boolean isWiFiConnected() {
+        return mWifiManager.isWifiEnabled() && mConnectivityManager.getActiveNetwork() != null
+                && !mConnectivityManager.isActiveNetworkMetered();
+    }
+
+    private class NetworkTracker extends ConnectivityManager.NetworkCallback {
+        private static final int MSG_CHECK_ACTIVE_NETWORK = 1;
+
+        private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
+
+        private final boolean mExpectedMetered;
+
+        private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == MSG_CHECK_ACTIVE_NETWORK) {
+                    checkActiveNetwork();
+                }
+            }
+        };
+
+        private NetworkTracker(boolean expectedMetered) {
+            mExpectedMetered = expectedMetered;
+        }
+
+        @Override
+        public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+                LinkProperties linkProperties, boolean blocked) {
+            checkActiveNetwork();
+        }
+
+        @Override
+        public void onLost(Network network) {
+            checkActiveNetwork();
+        }
+
+        boolean waitForExpectedState() throws InterruptedException {
+            checkActiveNetwork();
+            return mReceiveLatch.await(60, TimeUnit.SECONDS);
+        }
+
+        private void checkActiveNetwork() {
+            if (mReceiveLatch.getCount() == 0) {
+                return;
+            }
+
+            if (mConnectivityManager.getActiveNetwork() != null
+                    && mConnectivityManager.isActiveNetworkMetered() == mExpectedMetered) {
+                mReceiveLatch.countDown();
+            } else {
+                mHandler.sendEmptyMessageDelayed(MSG_CHECK_ACTIVE_NETWORK, 5000);
+            }
+        }
+    }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java
new file mode 100644
index 0000000..88cb719
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetrics.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests;
+
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PowerMetrics {
+    private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
+    private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
+    private static final String PACKAGE_SYSTEMUI = "com.android.systemui";
+    private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER,
+            PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
+
+    enum MetricKind {
+        POWER,
+        DURATION,
+    }
+
+    public static final String METRIC_APP_POWER = "appPower";
+    public static final String METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL = "appPowerExcludeSystem";
+    public static final String METRIC_APP_POWER_EXCLUDE_SMEARED = "appPowerExcludeSmeared";
+    public static final String METRIC_SCREEN_POWER = "screenPower";
+    public static final String METRIC_WIFI_POWER = "wifiPower";
+    public static final String METRIC_SYSTEM_SERVICE_CPU_POWER = "systemService";
+    public static final String METRIC_OTHER_POWER = "otherPower";
+    public static final String METRIC_CPU_POWER = "cpuPower";
+    public static final String METRIC_RAM_POWER = "ramPower";
+    public static final String METRIC_WAKELOCK_POWER = "wakelockPower";
+    public static final String METRIC_MOBILE_RADIO_POWER = "mobileRadioPower";
+    public static final String METRIC_BLUETOOTH_POWER = "bluetoothPower";
+    public static final String METRIC_GPS_POWER = "gpsPower";
+    public static final String METRIC_CAMERA_POWER = "cameraPower";
+    public static final String METRIC_FLASHLIGHT_POWER = "flashlightPower";
+    public static final String METRIC_SENSORS_POWER = "sensorsPower";
+    public static final String METRIC_AUDIO_POWER = "audioPower";
+    public static final String METRIC_VIDEO_POWER = "videoPower";
+    public static final String METRIC_CPU_TIME = "cpuTime";
+    public static final String METRIC_CPU_FOREGROUND_TIME = "cpuForegroundTime";
+    public static final String METRIC_WAKELOCK_TIME = "wakelockTime";
+    public static final String METRIC_WIFI_RUNNING_TIME = "wifiRunningTime";
+    public static final String METRIC_BLUETOOTH_RUNNING_TIME = "bluetoothRunningTime";
+    public static final String METRIC_GPS_TIME = "gpsTime";
+    public static final String METRIC_CAMERA_TIME = "cameraTime";
+    public static final String METRIC_FLASHLIGHT_TIME = "flashlightTime";
+    public static final String METRIC_AUDIO_TIME = "audioTime";
+    public static final String METRIC_VIDEO_TIME = "videoTime";
+
+    public static class Metric {
+        public String metricType;
+        public MetricKind metricKind;
+        public String title;
+        public double value;
+        public double total;
+    }
+
+    private final double mMinDrainedPower;
+    private final double mMaxDrainedPower;
+
+    private List<Metric> mMetrics = new ArrayList<>();
+
+    public PowerMetrics(BatteryStatsHelper batteryStatsHelper, int uid) {
+        mMinDrainedPower = batteryStatsHelper.getMinDrainedPower();
+        mMaxDrainedPower = batteryStatsHelper.getMaxDrainedPower();
+
+        List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
+
+        double totalPowerMah = 0;
+        double totalSmearedPowerMah = 0;
+        double totalPowerExcludeSystemMah = 0;
+        double totalScreenPower = 0;
+        double totalProportionalSmearMah = 0;
+        double totalCpuPowerMah = 0;
+        double totalSystemServiceCpuPowerMah = 0;
+        double totalUsagePowerMah = 0;
+        double totalWakeLockPowerMah = 0;
+        double totalMobileRadioPowerMah = 0;
+        double totalWifiPowerMah = 0;
+        double totalBluetoothPowerMah = 0;
+        double totalGpsPowerMah = 0;
+        double totalCameraPowerMah = 0;
+        double totalFlashlightPowerMah = 0;
+        double totalSensorPowerMah = 0;
+        double totalAudioPowerMah = 0;
+        double totalVideoPowerMah = 0;
+
+        long totalCpuTimeMs = 0;
+        long totalCpuFgTimeMs = 0;
+        long totalWakeLockTimeMs = 0;
+        long totalWifiRunningTimeMs = 0;
+        long totalBluetoothRunningTimeMs = 0;
+        long totalGpsTimeMs = 0;
+        long totalCameraTimeMs = 0;
+        long totalFlashlightTimeMs = 0;
+        long totalAudioTimeMs = 0;
+        long totalVideoTimeMs = 0;
+
+        BatterySipper uidSipper = null;
+        for (BatterySipper sipper : usageList) {
+            if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
+                totalScreenPower = sipper.sumPower();
+            }
+
+            if (isHiddenDrainType(sipper.drainType)) {
+                continue;
+            }
+
+            if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) {
+                uidSipper = sipper;
+            }
+
+            totalPowerMah += sipper.sumPower();
+            totalSmearedPowerMah += sipper.totalSmearedPowerMah;
+            totalProportionalSmearMah += sipper.proportionalSmearMah;
+
+            if (!isSystemSipper(sipper)) {
+                totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah;
+            }
+
+            totalCpuPowerMah += sipper.cpuPowerMah;
+            totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah;
+            totalUsagePowerMah += sipper.usagePowerMah;
+            totalWakeLockPowerMah += sipper.wakeLockPowerMah;
+            totalMobileRadioPowerMah += sipper.mobileRadioPowerMah;
+            totalWifiPowerMah += sipper.wifiPowerMah;
+            totalBluetoothPowerMah += sipper.bluetoothPowerMah;
+            totalGpsPowerMah += sipper.gpsPowerMah;
+            totalCameraPowerMah += sipper.cameraPowerMah;
+            totalFlashlightPowerMah += sipper.flashlightPowerMah;
+            totalSensorPowerMah += sipper.sensorPowerMah;
+            totalAudioPowerMah += sipper.audioPowerMah;
+            totalVideoPowerMah += sipper.videoPowerMah;
+
+            totalCpuTimeMs += sipper.cpuTimeMs;
+            totalCpuFgTimeMs += sipper.cpuFgTimeMs;
+            totalWakeLockTimeMs += sipper.wakeLockTimeMs;
+            totalWifiRunningTimeMs += sipper.wifiRunningTimeMs;
+            totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs;
+            totalGpsTimeMs += sipper.gpsTimeMs;
+            totalCameraTimeMs += sipper.cameraTimeMs;
+            totalFlashlightTimeMs += sipper.flashlightTimeMs;
+            totalAudioTimeMs += sipper.audioTimeMs;
+            totalVideoTimeMs += sipper.videoTimeMs;
+        }
+
+        if (uidSipper == null) {
+            return;
+        }
+
+        addMetric(METRIC_APP_POWER, MetricKind.POWER, "Total power",
+                uidSipper.totalSmearedPowerMah, totalSmearedPowerMah);
+        addMetric(METRIC_APP_POWER_EXCLUDE_SYSTEM_FROM_TOTAL, MetricKind.POWER,
+                "Total power excluding system",
+                uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
+        addMetric(METRIC_SCREEN_POWER, MetricKind.POWER, "Screen, smeared",
+                uidSipper.screenPowerMah, totalScreenPower);
+        addMetric(METRIC_OTHER_POWER, MetricKind.POWER, "Other, smeared",
+                uidSipper.proportionalSmearMah, totalProportionalSmearMah);
+        addMetric(METRIC_APP_POWER_EXCLUDE_SMEARED, MetricKind.POWER, "Excluding smeared",
+                uidSipper.totalPowerMah, totalPowerMah);
+        addMetric(METRIC_CPU_POWER, MetricKind.POWER, "CPU",
+                uidSipper.cpuPowerMah, totalCpuPowerMah);
+        addMetric(METRIC_SYSTEM_SERVICE_CPU_POWER, MetricKind.POWER, "System services",
+                uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
+        addMetric(METRIC_RAM_POWER, MetricKind.POWER, "RAM",
+                uidSipper.usagePowerMah, totalUsagePowerMah);
+        addMetric(METRIC_WAKELOCK_POWER, MetricKind.POWER, "Wake lock",
+                uidSipper.wakeLockPowerMah, totalWakeLockPowerMah);
+        addMetric(METRIC_MOBILE_RADIO_POWER, MetricKind.POWER, "Mobile radio",
+                uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
+        addMetric(METRIC_WIFI_POWER, MetricKind.POWER, "WiFi",
+                uidSipper.wifiPowerMah, totalWifiPowerMah);
+        addMetric(METRIC_BLUETOOTH_POWER, MetricKind.POWER, "Bluetooth",
+                uidSipper.bluetoothPowerMah, totalBluetoothPowerMah);
+        addMetric(METRIC_GPS_POWER, MetricKind.POWER, "GPS",
+                uidSipper.gpsPowerMah, totalGpsPowerMah);
+        addMetric(METRIC_CAMERA_POWER, MetricKind.POWER, "Camera",
+                uidSipper.cameraPowerMah, totalCameraPowerMah);
+        addMetric(METRIC_FLASHLIGHT_POWER, MetricKind.POWER, "Flashlight",
+                uidSipper.flashlightPowerMah, totalFlashlightPowerMah);
+        addMetric(METRIC_SENSORS_POWER, MetricKind.POWER, "Sensors",
+                uidSipper.sensorPowerMah, totalSensorPowerMah);
+        addMetric(METRIC_AUDIO_POWER, MetricKind.POWER, "Audio",
+                uidSipper.audioPowerMah, totalAudioPowerMah);
+        addMetric(METRIC_VIDEO_POWER, MetricKind.POWER, "Video",
+                uidSipper.videoPowerMah, totalVideoPowerMah);
+
+        addMetric(METRIC_CPU_TIME, MetricKind.DURATION, "CPU time",
+                uidSipper.cpuTimeMs, totalCpuTimeMs);
+        addMetric(METRIC_CPU_FOREGROUND_TIME, MetricKind.DURATION, "CPU foreground time",
+                uidSipper.cpuFgTimeMs, totalCpuFgTimeMs);
+        addMetric(METRIC_WAKELOCK_TIME, MetricKind.DURATION, "Wake lock time",
+                uidSipper.wakeLockTimeMs, totalWakeLockTimeMs);
+        addMetric(METRIC_WIFI_RUNNING_TIME, MetricKind.DURATION, "WiFi running time",
+                uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
+        addMetric(METRIC_BLUETOOTH_RUNNING_TIME, MetricKind.DURATION, "Bluetooth time",
+                uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
+        addMetric(METRIC_GPS_TIME, MetricKind.DURATION, "GPS time",
+                uidSipper.gpsTimeMs, totalGpsTimeMs);
+        addMetric(METRIC_CAMERA_TIME, MetricKind.DURATION, "Camera time",
+                uidSipper.cameraTimeMs, totalCameraTimeMs);
+        addMetric(METRIC_FLASHLIGHT_TIME, MetricKind.DURATION, "Flashlight time",
+                uidSipper.flashlightTimeMs, totalFlashlightTimeMs);
+        addMetric(METRIC_AUDIO_TIME, MetricKind.DURATION, "Audio time",
+                uidSipper.audioTimeMs, totalAudioTimeMs);
+        addMetric(METRIC_VIDEO_TIME, MetricKind.DURATION, "Video time",
+                uidSipper.videoTimeMs, totalVideoTimeMs);
+    }
+
+    public List<Metric> getMetrics() {
+        return mMetrics;
+    }
+
+    public double getMinDrainedPower() {
+        return mMinDrainedPower;
+    }
+
+    public double getMaxDrainedPower() {
+        return mMaxDrainedPower;
+    }
+
+    protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) {
+        return drainType == BatterySipper.DrainType.IDLE
+                || drainType == BatterySipper.DrainType.CELL
+                || drainType == BatterySipper.DrainType.SCREEN
+                || drainType == BatterySipper.DrainType.UNACCOUNTED
+                || drainType == BatterySipper.DrainType.OVERCOUNTED
+                || drainType == BatterySipper.DrainType.BLUETOOTH
+                || drainType == BatterySipper.DrainType.WIFI;
+    }
+
+    private boolean isSystemSipper(BatterySipper sipper) {
+        final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
+        if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
+            return true;
+        } else if (sipper.mPackages != null) {
+            for (final String packageName : sipper.mPackages) {
+                for (final String systemPackage : PACKAGES_SYSTEM) {
+                    if (systemPackage.equals(packageName)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private void addMetric(String metricType, MetricKind metricKind, String title, double amount,
+            double totalAmount) {
+        Metric metric = new Metric();
+        metric.metricType = metricType;
+        metric.metricKind = metricKind;
+        metric.title = title;
+        metric.value = amount;
+        metric.total = totalAmount;
+        mMetrics.add(metric);
+    }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
new file mode 100644
index 0000000..0cdb404
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests;
+
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.TimeUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.internal.os.LoggingPrintStream;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class PowerMetricsCollector implements TestRule {
+    private final String mTag;
+    private final float mBatteryDrainThresholdPct;
+    private final int mTimeoutMillis;
+
+    private final Context mContext;
+    private final UserManager mUserManager;
+    private final int mUid;
+    private final BatteryStatsHelper mStatsHelper;
+
+    private long mStartTime;
+    private volatile float mInitialBatteryLevel;
+    private volatile float mCurrentBatteryLevel;
+    private int mIterations;
+    private PowerMetrics mInitialPowerMetrics;
+    private PowerMetrics mFinalPowerMetrics;
+    private List<PowerMetrics.Metric> mPowerMetricsDelta;
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                disableCharger();
+                try {
+                    prepareBatteryLevelMonitor();
+                    mStartTime = SystemClock.uptimeMillis();
+                    base.evaluate();
+                    captureFinalPowerStatsData();
+                } finally {
+                    enableCharger();
+                }
+            }
+        };
+    }
+
+    public PowerMetricsCollector(String tag, float batteryDrainThresholdPct, int timeoutMillis) {
+        mTag = tag;
+        mBatteryDrainThresholdPct = batteryDrainThresholdPct;
+        mTimeoutMillis = timeoutMillis;
+
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = instrumentation.getContext();
+        mUid = Process.myUid();
+        mUserManager = mContext.getSystemService(UserManager.class);
+        mStatsHelper = new BatteryStatsHelper(mContext, false /* collectBatteryBroadcast */);
+        mStatsHelper.create((Bundle) null);
+    }
+
+    private void disableCharger() {
+        // TODO(b/167636754): implement this method once the charger suspension API is available
+    }
+
+    private void enableCharger() {
+        // TODO(b/167636754): implement this method once the charger suspension API is available
+    }
+
+    private PowerMetrics readBatteryStatsData() {
+        mStatsHelper.clearStats();
+        mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+                mUserManager.getUserProfiles());
+        return new PowerMetrics(mStatsHelper, mUid);
+    }
+
+    protected void prepareBatteryLevelMonitor() {
+        Intent batteryStatus = mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                handleBatteryStatus(intent);
+            }
+        }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+        handleBatteryStatus(batteryStatus);
+        mInitialBatteryLevel = mCurrentBatteryLevel;
+    }
+
+    protected void handleBatteryStatus(Intent intent) {
+        if (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0) {
+            fail("Device must remain disconnected from the power source "
+                    + "for the duration of the test");
+        }
+
+        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+        mCurrentBatteryLevel = level * 100 / (float) scale;
+        Log.i(mTag, "Battery level = " + mCurrentBatteryLevel);
+
+        // We delay tracking until the battery level drops.  If the resolution of
+        // battery level is 1%, and the initially reported level is 73, we don't know whether
+        // it's 73.1 or 73.7. Once it drops to 72, we can be confident that the real battery
+        // level it is very close to 72.0 and can start tracking.
+        if (mInitialPowerMetrics == null && mCurrentBatteryLevel < mInitialBatteryLevel) {
+            mInitialBatteryLevel = mCurrentBatteryLevel;
+            mInitialPowerMetrics = readBatteryStatsData();
+        }
+    }
+
+    private void captureFinalPowerStatsData() {
+        if (mFinalPowerMetrics != null) {
+            return;
+        }
+
+        mFinalPowerMetrics = readBatteryStatsData();
+
+        mPowerMetricsDelta = new ArrayList<>();
+        List<PowerMetrics.Metric> initialPowerMetrics = mInitialPowerMetrics.getMetrics();
+        List<PowerMetrics.Metric> finalPowerMetrics = mFinalPowerMetrics.getMetrics();
+        for (PowerMetrics.Metric initialMetric : initialPowerMetrics) {
+            PowerMetrics.Metric finalMetric = null;
+            for (PowerMetrics.Metric metric : finalPowerMetrics) {
+                if (metric.title.equals(initialMetric.title)) {
+                    finalMetric = metric;
+                    break;
+                }
+            }
+
+            if (finalMetric != null) {
+                PowerMetrics.Metric delta = new PowerMetrics.Metric();
+                delta.metricType = initialMetric.metricType;
+                delta.metricKind = initialMetric.metricKind;
+                delta.title = initialMetric.title;
+                delta.total = finalMetric.total - initialMetric.total;
+                delta.value = finalMetric.value - initialMetric.value;
+                mPowerMetricsDelta.add(delta);
+            }
+        }
+    }
+
+    /**
+     * Returns false if sufficient data has been accumulated.
+     */
+    public boolean checkpoint() {
+        long elapsedTime = SystemClock.uptimeMillis() - mStartTime;
+        if (elapsedTime >= mTimeoutMillis) {
+            Log.i(mTag, "Timeout reached " + TimeUtils.formatDuration(elapsedTime));
+            captureFinalPowerStatsData();
+            return false;
+        }
+
+        if (mInitialPowerMetrics == null) {
+            return true;
+        }
+
+        if (mInitialBatteryLevel - mCurrentBatteryLevel >= mBatteryDrainThresholdPct) {
+            Log.i(mTag,
+                    "Battery drain reached " + (mInitialBatteryLevel - mCurrentBatteryLevel) + "%");
+            captureFinalPowerStatsData();
+            return false;
+        }
+
+        mIterations++;
+        return true;
+    }
+
+
+    public int getIterationCount() {
+        return mIterations;
+    }
+
+    public void dumpMetrics() {
+        dumpMetrics(new LoggingPrintStream() {
+            @Override
+            protected void log(String line) {
+                Log.i(mTag, line);
+            }
+        });
+    }
+
+    public void dumpMetrics(PrintStream out) {
+        List<PowerMetrics.Metric> initialPowerMetrics = mInitialPowerMetrics.getMetrics();
+        List<PowerMetrics.Metric> finalPowerMetrics = mFinalPowerMetrics.getMetrics();
+
+        out.println("== Power metrics at test start");
+        dumpPowerStatsData(out, initialPowerMetrics);
+
+        out.println("== Power metrics at test end");
+        dumpPowerStatsData(out, finalPowerMetrics);
+
+        out.println("== Power metrics delta");
+        dumpPowerStatsData(out, mPowerMetricsDelta);
+    }
+
+    protected void dumpPowerStatsData(PrintStream out, List<PowerMetrics.Metric> metrics) {
+        Locale locale = Locale.getDefault();
+        for (PowerMetrics.Metric metric : metrics) {
+            double proportion = metric.total != 0 ? metric.value * 100 / metric.total : 0;
+            switch (metric.metricKind) {
+                case POWER:
+                    out.println(
+                            String.format(locale, "    %-30s %7.1f mAh %4.1f%%", metric.title,
+                                    metric.value, proportion));
+                    break;
+                case DURATION:
+                    out.println(
+                            String.format(locale, "    %-30s %,7d ms  %4.1f%%", metric.title,
+                                    (long) metric.value, proportion));
+                    break;
+            }
+        }
+    }
+
+    public void dumpMetricAsPercentageOfDrainedPower(String metricType) {
+        double minDrainedPower =
+                mFinalPowerMetrics.getMinDrainedPower() - mInitialPowerMetrics.getMinDrainedPower();
+        double maxDrainedPower =
+                mFinalPowerMetrics.getMaxDrainedPower() - mInitialPowerMetrics.getMaxDrainedPower();
+
+        PowerMetrics.Metric metric = getMetric(metricType);
+        double metricDelta = metric.value;
+
+        if (maxDrainedPower - minDrainedPower < 0.1f) {
+            Log.i(mTag, String.format(Locale.getDefault(),
+                    "%s power consumed by the test: %.1f of %.1f mAh (%.1f%%)",
+                    metric.title, metricDelta, maxDrainedPower,
+                    metricDelta / maxDrainedPower * 100));
+        } else {
+            Log.i(mTag, String.format(Locale.getDefault(),
+                    "%s power consumed by the test: %.1f of %.1f - %.1f mAh (%.1f%% - %.1f%%)",
+                    metric.title, metricDelta, minDrainedPower, maxDrainedPower,
+                    metricDelta / minDrainedPower * 100, metricDelta / maxDrainedPower * 100));
+        }
+    }
+
+    public PowerMetrics.Metric getMetric(String metricType) {
+        for (PowerMetrics.Metric metric : mPowerMetricsDelta) {
+            if (metric.metricType.equals(metricType)) {
+                return metric;
+            }
+        }
+        return null;
+    }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java
new file mode 100644
index 0000000..911ccba
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/SystemServiceCallLoadTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class SystemServiceCallLoadTest {
+    private static final String TAG = "SystemServiceCallLoadTest";
+    private static final int TIMEOUT_MILLIS = 60 * 60 * 1000;
+    private static final float BATTERY_DRAIN_THRESHOLD_PCT = 2.99f;
+
+    @Rule
+    public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG,
+            BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS);
+
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setup() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mPackageManager = instrumentation.getContext().getPackageManager();
+    }
+
+    @Test
+    public void test() {
+        while (mPowerMetricsCollector.checkpoint()) {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.setDataAndType(Uri.parse("http://example.com/"), "text/plain");
+            intent.addCategory(Intent.CATEGORY_BROWSABLE);
+            ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
+            assertNotNull(resolveInfo);
+        }
+
+        mPowerMetricsCollector.dumpMetrics();
+
+        Log.i(TAG, "==");
+        Log.i(TAG, "Total system server calls made " + mPowerMetricsCollector.getIterationCount());
+
+        mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower(
+                PowerMetrics.METRIC_SYSTEM_SERVICE_CPU_POWER);
+    }
+}
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java
new file mode 100644
index 0000000..90627192
--- /dev/null
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/WiFiLoadTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 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.frameworks.core.powerstatsloadtests;
+
+import android.util.Log;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+public class WiFiLoadTest {
+    private static final String TAG = "WiFiLoadTest";
+    private static final String DOWNLOAD_TEST_URL =
+            "https://i.ytimg.com/vi/l5mE3Tpjejs/maxresdefault.jpg";
+
+    private static final int TIMEOUT_MILLIS = 60 * 60 * 1000;
+    private static final float BATTERY_DRAIN_THRESHOLD_PCT = 0.99f;
+
+    @Rule
+    public PowerMetricsCollector mPowerMetricsCollector = new PowerMetricsCollector(TAG,
+            BATTERY_DRAIN_THRESHOLD_PCT, TIMEOUT_MILLIS);
+
+    @Rule
+    public ConnectivitySetupRule mConnectivitySetupRule =
+            new ConnectivitySetupRule(/* WiFi enabled */true);
+
+    @Test
+    public void test() throws IOException {
+        long totalBytesRead = 0;
+        URL url = new URL(DOWNLOAD_TEST_URL);
+        byte[] buffer = new byte[131072];  // Large buffer to minimize CPU usage
+
+        while (mPowerMetricsCollector.checkpoint()) {
+            try (InputStream inputStream = url.openStream()) {
+                while (true) {
+                    int count = inputStream.read(buffer);
+                    if (count < 0) {
+                        break;
+                    }
+                    totalBytesRead += count;
+                }
+            }
+        }
+
+        mPowerMetricsCollector.dumpMetrics();
+
+        Log.i(TAG, "==");
+        Log.i(TAG, "WiFi running time: " + (long) mPowerMetricsCollector.getMetric(
+                PowerMetrics.METRIC_WIFI_RUNNING_TIME).value);
+        Log.i(TAG, "Total bytes read over WiFi: " + totalBytesRead);
+
+        mPowerMetricsCollector.dumpMetricAsPercentageOfDrainedPower(
+                PowerMetrics.METRIC_WIFI_POWER);
+    }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 03f7be7..e693198 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -43,6 +43,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-2049725903": {
+      "message": "Task back pressed on root taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-2039580386": {
       "message": "Attempted to add input method window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -85,6 +91,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
     },
+    "-1980468143": {
+      "message": "DisplayArea appeared name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "-1976930686": {
       "message": "Attempted to add Accessibility overlay window with bad token %s.  Aborting.",
       "level": "WARN",
@@ -103,6 +115,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1939861963": {
+      "message": "Create root task displayId=%d winMode=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-1939358269": {
       "message": "mRecentScreenshotAnimator finish",
       "level": "DEBUG",
@@ -127,6 +145,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1895337367": {
+      "message": "Delete root task display=%d winMode=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-1884933373": {
       "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
       "level": "INFO",
@@ -187,6 +211,12 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
     },
+    "-1792633344": {
+      "message": "Register task organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-1791031393": {
       "message": "Ensuring correct configuration: %s",
       "level": "VERBOSE",
@@ -295,12 +325,6 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
-    "-1587841219": {
-      "message": "Focus moving from %s to %s displayId=%d",
-      "level": "INFO",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "-1568331821": {
       "message": "Enabling listeners",
       "level": "VERBOSE",
@@ -385,6 +409,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1438175584": {
+      "message": "Input focus has changed to %s display=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/InputMonitor.java"
+    },
     "-1434147454": {
       "message": "cleanupAnimation(): Notify animation finished mPendingAnimations=%d reorderMode=%d",
       "level": "DEBUG",
@@ -421,6 +451,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1364754753": {
+      "message": "Task vanished taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-1352076759": {
       "message": "Removing app token: %s",
       "level": "VERBOSE",
@@ -529,6 +565,12 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
+    "-1142279614": {
+      "message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "-1130891072": {
       "message": "Orientation continue waiting for draw in %s",
       "level": "VERBOSE",
@@ -643,6 +685,18 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-951939129": {
+      "message": "Unregister task organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
+    "-930893991": {
+      "message": "Set sync ready, syncId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+    },
     "-929676529": {
       "message": "Configuration changes for %s, allChanges=%s",
       "level": "VERBOSE",
@@ -751,12 +805,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-771282525": {
-      "message": "Losing focus: %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "-771177730": {
       "message": "Removing focused app token:%s displayId=%d",
       "level": "VERBOSE",
@@ -793,24 +841,12 @@
       "group": "WM_DEBUG_FOCUS",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
-    "-714291355": {
-      "message": "Losing delayed focus: %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "-694710814": {
       "message": "Pausing rotation during drag",
       "level": "DEBUG",
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DragState.java"
     },
-    "-687185281": {
-      "message": "New topFocusedDisplayId=%d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
-    },
     "-668956537": {
       "message": "  THUMBNAIL %s: CREATE",
       "level": "INFO",
@@ -835,6 +871,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowToken.java"
     },
+    "-639217716": {
+      "message": "setFocusedApp %s displayId=%d Callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "-635082269": {
       "message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b",
       "level": "INFO",
@@ -853,12 +895,6 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-603199586": {
-      "message": "Clearing focused app, displayId=%d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "-593535526": {
       "message": "Binding proc %s with config %s",
       "level": "VERBOSE",
@@ -877,6 +913,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "-561092364": {
+      "message": "onPointerDownOutsideFocusLocked called on %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "-549028919": {
       "message": "enableScreenIfNeededLocked: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
       "level": "INFO",
@@ -919,6 +961,12 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
     },
+    "-497620140": {
+      "message": "Transaction ready, syncId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+    },
     "-496681057": {
       "message": "Attempted to get remove mode of a display that does not exist: %d",
       "level": "WARN",
@@ -1153,12 +1201,6 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
-    "-96848838": {
-      "message": "Gaining focus: %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "-90559682": {
       "message": "Config is skipping already pausing %s",
       "level": "VERBOSE",
@@ -1315,6 +1357,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "115358443": {
+      "message": "Focus changing: %s -> %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "123161180": {
       "message": "SEVER CHILDREN",
       "level": "INFO",
@@ -1345,6 +1393,18 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/BlackFrame.java"
     },
+    "155482615": {
+      "message": "Focus requested for window=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/InputMonitor.java"
+    },
+    "174572959": {
+      "message": "DisplayArea info changed name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "184362060": {
       "message": "screenshotTask(%d): mCanceled=%b",
       "level": "DEBUG",
@@ -1387,6 +1447,12 @@
       "group": "WM_DEBUG_KEEP_SCREEN_ON",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
+    "232317536": {
+      "message": "Set intercept back pressed on root=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "241961619": {
       "message": "Adding %s to %s",
       "level": "VERBOSE",
@@ -1405,6 +1471,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "251812577": {
+      "message": "Register display organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "254883724": {
       "message": "addWindowToken: Attempted to add binder token: %s for already created window token: %s displayId=%d",
       "level": "WARN",
@@ -1429,12 +1501,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/AppTransition.java"
     },
-    "285317231": {
-      "message": "Input focus has changed to %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/InputMonitor.java"
-    },
     "288485303": {
       "message": "Attempted to set remove mode to a display that does not exist: %d",
       "level": "WARN",
@@ -1453,6 +1519,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "302969511": {
+      "message": "Task info changed taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "302992539": {
       "message": "addAnimation(%s)",
       "level": "DEBUG",
@@ -1465,6 +1537,12 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
     },
+    "312030608": {
+      "message": "New topFocusedDisplayId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+    },
     "315395835": {
       "message": "Trying to add window with invalid user=%d",
       "level": "WARN",
@@ -1519,12 +1597,6 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "385096046": {
-      "message": "Delaying loss of focus...",
-      "level": "INFO",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "399841913": {
       "message": "SURFACE RECOVER DESTROY: %s",
       "level": "INFO",
@@ -1573,6 +1645,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "487621047": {
+      "message": "DisplayArea vanished name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "490877640": {
       "message": "onStackOrderChanged(): stack=%s",
       "level": "DEBUG",
@@ -1627,12 +1705,6 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
     },
-    "584499099": {
-      "message": "Set focused app to: %s moveFocusNow=%b displayId=%d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "585096182": {
       "message": "SURFACE isColorSpaceAgnostic=%b: %s",
       "level": "INFO",
@@ -1675,6 +1747,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "620519522": {
+      "message": "findFocusedWindow: No focusable windows, display=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "628276090": {
       "message": "Delaying app transition for screen rotation animation to finish",
       "level": "VERBOSE",
@@ -1831,6 +1909,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "872933199": {
+      "message": "Changing focus from %s to %s displayId=%d Callers=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "873914452": {
       "message": "goodToGo()",
       "level": "DEBUG",
@@ -1849,6 +1933,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "906215061": {
+      "message": "Apply window transaction, syncId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+    },
     "913494177": {
       "message": "removeAllWindowsIfPossible: removing win=%s",
       "level": "WARN",
@@ -1957,6 +2047,12 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/DisplayPolicy.java"
     },
+    "1149424314": {
+      "message": "Unregister display organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "1160771501": {
       "message": "Resize reasons for w=%s:  %s surfaceResized=%b configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
       "level": "VERBOSE",
@@ -2053,12 +2149,6 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
     },
-    "1358462645": {
-      "message": "Looking for focus: %s, flags=%d, canReceive=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "1360551978": {
       "message": "Trying to update display configuration for non-existing displayId=%d",
       "level": "WARN",
@@ -2149,12 +2239,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
-    "1469292670": {
-      "message": "Changing focus from %s to %s displayId=%d Callers=%s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "1495525537": {
       "message": "createWallpaperAnimations()",
       "level": "DEBUG",
@@ -2443,6 +2527,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1918448345": {
+      "message": "Task appeared taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "1921821199": {
       "message": "Preserving %s until the new one is added",
       "level": "VERBOSE",
@@ -2551,6 +2641,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
     },
+    "2081291430": {
+      "message": "Focus not requested for window=%s because it has no surface",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS_LIGHT",
+      "at": "com\/android\/server\/wm\/InputMonitor.java"
+    },
     "2083556954": {
       "message": "Set mOrientationChanging of %s",
       "level": "VERBOSE",
@@ -2587,12 +2683,6 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "2128604122": {
-      "message": "findFocusedWindow: No focusable windows.",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "2128917433": {
       "message": "onProposedRotationChanged, rotation=%d",
       "level": "VERBOSE",
@@ -2676,6 +2766,9 @@
     "WM_DEBUG_WINDOW_MOVEMENT": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_WINDOW_ORGANIZER": {
+      "tag": "WindowManager"
+    },
     "WM_ERROR": {
       "tag": "WindowManager"
     },
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 1ab4c63..fe6eeeb 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -28,12 +28,14 @@
     label:                              'A'
     base:                               'a'
     shift, capslock:                    'A'
+    shift+capslock:                     'a'
 }
 
 key B {
     label:                              'B'
     base:                               'b'
     shift, capslock:                    'B'
+    shift+capslock:                     'b'
 }
 
 key C {
@@ -42,12 +44,14 @@
     shift, capslock:                    'C'
     alt:                                '\u00e7'
     shift+alt:                          '\u00c7'
+    shift+capslock:                     'c'
 }
 
 key D {
     label:                              'D'
     base:                               'd'
     shift, capslock:                    'D'
+    shift+capslock:                     'd'
 }
 
 key E {
@@ -55,24 +59,28 @@
     base:                               'e'
     shift, capslock:                    'E'
     alt:                                '\u0301'
+    shift+capslock:                     'e'
 }
 
 key F {
     label:                              'F'
     base:                               'f'
     shift, capslock:                    'F'
+    shift+capslock:                     'f'
 }
 
 key G {
     label:                              'G'
     base:                               'g'
     shift, capslock:                    'G'
+    shift+capslock:                     'g'
 }
 
 key H {
     label:                              'H'
     base:                               'h'
     shift, capslock:                    'H'
+    shift+capslock:                     'h'
 }
 
 key I {
@@ -80,30 +88,35 @@
     base:                               'i'
     shift, capslock:                    'I'
     alt:                                '\u0302'
+    shift+capslock:                     'i'
 }
 
 key J {
     label:                              'J'
     base:                               'j'
     shift, capslock:                    'J'
+    shift+capslock:                     'j'
 }
 
 key K {
     label:                              'K'
     base:                               'k'
     shift, capslock:                    'K'
+    shift+capslock:                     'k'
 }
 
 key L {
     label:                              'L'
     base:                               'l'
     shift, capslock:                    'L'
+    shift+capslock:                     'l'
 }
 
 key M {
     label:                              'M'
     base:                               'm'
     shift, capslock:                    'M'
+    shift+capslock:                     'm'
 }
 
 key N {
@@ -111,30 +124,35 @@
     base:                               'n'
     shift, capslock:                    'N'
     alt:                                '\u0303'
+    shift+capslock:                     'n'
 }
 
 key O {
     label:                              'O'
     base:                               'o'
     shift, capslock:                    'O'
+    shift+capslock:                     'o'
 }
 
 key P {
     label:                              'P'
     base:                               'p'
     shift, capslock:                    'P'
+    shift+capslock:                     'p'
 }
 
 key Q {
     label:                              'Q'
     base:                               'q'
     shift, capslock:                    'Q'
+    shift+capslock:                     'q'
 }
 
 key R {
     label:                              'R'
     base:                               'r'
     shift, capslock:                    'R'
+    shift+capslock:                     'r'
 }
 
 key S {
@@ -142,12 +160,14 @@
     base:                               's'
     shift, capslock:                    'S'
     alt:                                '\u00df'
+    shift+capslock:                     's'
 }
 
 key T {
     label:                              'T'
     base:                               't'
     shift, capslock:                    'T'
+    shift+capslock:                     't'
 }
 
 key U {
@@ -155,36 +175,42 @@
     base:                               'u'
     shift, capslock:                    'U'
     alt:                                '\u0308'
+    shift+capslock:                     'u'
 }
 
 key V {
     label:                              'V'
     base:                               'v'
     shift, capslock:                    'V'
+    shift+capslock:                     'v'
 }
 
 key W {
     label:                              'W'
     base:                               'w'
     shift, capslock:                    'W'
+    shift+capslock:                     'w'
 }
 
 key X {
     label:                              'X'
     base:                               'x'
     shift, capslock:                    'X'
+    shift+capslock:                     'x'
 }
 
 key Y {
     label:                              'Y'
     base:                               'y'
     shift, capslock:                    'Y'
+    shift+capslock:                     'y'
 }
 
 key Z {
     label:                              'Z'
     base:                               'z'
     shift, capslock:                    'Z'
+    shift+capslock:                     'z'
 }
 
 key 0 {
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
index 70dedb8..d8af726 100644
--- a/framework-jarjar-rules.txt
+++ b/framework-jarjar-rules.txt
@@ -1,6 +1,2 @@
 rule android.hidl.** android.internal.hidl.@1
 rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
-
-# Hide media mainline module implementation classes to avoid collisions with
-# app-bundled ExoPlayer classes.
-rule com.google.android.exoplayer2.** android.media.internal.exo.@1
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 843b177..1591b06 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -12,14 +12,100 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Begin ProtoLog
+java_library {
+    name: "wm_shell_protolog-groups",
+    srcs: [
+        "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
+        ":protolog-common-src",
+    ],
+}
+
+filegroup {
+    name: "wm_shell-sources",
+    srcs: ["src/**/*.java"],
+    path: "src",
+}
+
+genrule {
+    name: "wm_shell_protolog_src",
+    srcs: [
+        ":wm_shell_protolog-groups",
+        ":wm_shell-sources",
+    ],
+    tools: ["protologtool"],
+    cmd: "$(location protologtool) transform-protolog-calls " +
+      "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+      "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
+      "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
+      "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+      "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+      "--output-srcjar $(out) " +
+      "$(locations :wm_shell-sources)",
+    out: ["wm_shell_protolog.srcjar"],
+}
+
+genrule {
+    name: "generate-wm_shell_protolog.json",
+    srcs: [
+        ":wm_shell_protolog-groups",
+        ":wm_shell-sources",
+    ],
+    tools: ["protologtool"],
+    cmd: "$(location protologtool) generate-viewer-config " +
+      "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+      "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+      "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+      "--viewer-conf $(out) " +
+      "$(locations :wm_shell-sources)",
+    out: ["wm_shell_protolog.json"],
+}
+
+filegroup {
+    name: "wm_shell_protolog.json",
+    srcs: ["res/raw/wm_shell_protolog.json"],
+}
+
+genrule {
+    name: "checked-wm_shell_protolog.json",
+    srcs: [
+        ":generate-wm_shell_protolog.json",
+        ":wm_shell_protolog.json",
+    ],
+    cmd: "cp $(location :generate-wm_shell_protolog.json) $(out) && " +
+      "{ ! (diff $(out) $(location :wm_shell_protolog.json) | grep -q '^<') || " +
+      "{ echo -e '\\n\\n################################################################\\n#\\n" +
+      "#  ERROR: ProtoLog viewer config is stale.  To update it, run:\\n#\\n" +
+      "#  cp $(location :generate-wm_shell_protolog.json) " +
+      "$(location :wm_shell_protolog.json)\\n#\\n" +
+      "################################################################\\n\\n' >&2 && false; } }",
+    out: ["wm_shell_protolog.json"],
+}
+// End ProtoLog
+
+java_library {
+    name: "WindowManager-Shell-proto",
+
+    srcs: ["proto/*.proto"],
+
+    proto: {
+        type: "nano",
+    },
+}
+
 android_library {
     name: "WindowManager-Shell",
     srcs: [
-        "src/**/*.java",
+        ":wm_shell_protolog_src",
         "src/**/I*.aidl",
     ],
     resource_dirs: [
         "res",
     ],
+    static_libs: [
+        "protolog-lib",
+        "WindowManager-Shell-proto",
+        "androidx.appcompat_appcompat",
+    ],
     manifest: "AndroidManifest.xml",
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/libs/WindowManager/Shell/proto/wm_shell_trace.proto
similarity index 76%
copy from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
copy to libs/WindowManager/Shell/proto/wm_shell_trace.proto
index 9d05843..b9e7252 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/libs/WindowManager/Shell/proto/wm_shell_trace.proto
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.settings
+syntax = "proto2";
 
-import android.content.ContentResolver
+package com.android.wm.shell;
 
-interface CurrentUserContentResolverProvider {
+option java_multiple_files = true;
 
-    val currentUserContentResolver: ContentResolver
-}
\ No newline at end of file
+message WmShellTraceProto {
+
+    // Not used, just a test value
+    optional bool test_value = 1;
+}
diff --git a/packages/SystemUI/res/anim/forced_resizable_enter.xml b/libs/WindowManager/Shell/res/anim/forced_resizable_enter.xml
similarity index 100%
rename from packages/SystemUI/res/anim/forced_resizable_enter.xml
rename to libs/WindowManager/Shell/res/anim/forced_resizable_enter.xml
diff --git a/packages/SystemUI/res/anim/forced_resizable_exit.xml b/libs/WindowManager/Shell/res/anim/forced_resizable_exit.xml
similarity index 100%
rename from packages/SystemUI/res/anim/forced_resizable_exit.xml
rename to libs/WindowManager/Shell/res/anim/forced_resizable_exit.xml
diff --git a/packages/SystemUI/res/drawable-hdpi/one_handed_tutorial.png b/libs/WindowManager/Shell/res/drawable-hdpi/one_handed_tutorial.png
similarity index 100%
rename from packages/SystemUI/res/drawable-hdpi/one_handed_tutorial.png
rename to libs/WindowManager/Shell/res/drawable-hdpi/one_handed_tutorial.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/divider.xml b/libs/WindowManager/Shell/res/layout/divider.xml
similarity index 100%
rename from packages/SystemUI/res/layout/divider.xml
rename to libs/WindowManager/Shell/res/layout/divider.xml
diff --git a/packages/SystemUI/res/layout/docked_stack_divider.xml b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
similarity index 85%
rename from packages/SystemUI/res/layout/docked_stack_divider.xml
rename to libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
index 70e5451..ad87025 100644
--- a/packages/SystemUI/res/layout/docked_stack_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<com.android.systemui.stackdivider.DividerView
+<com.android.wm.shell.splitscreen.DividerView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_height="match_parent"
         android:layout_width="match_parent">
@@ -24,15 +24,15 @@
         android:id="@+id/docked_divider_background"
         android:background="@color/docked_divider_background"/>
 
-    <com.android.systemui.stackdivider.MinimizedDockShadow
+    <com.android.wm.shell.splitscreen.MinimizedDockShadow
         style="@style/DockedDividerMinimizedShadow"
         android:id="@+id/minimized_dock_shadow"
         android:alpha="0"/>">
 
-    <com.android.systemui.stackdivider.DividerHandleView
+    <com.android.wm.shell.splitscreen.DividerHandleView
         style="@style/DockedDividerHandle"
         android:id="@+id/docked_divider_handle"
         android:contentDescription="@string/accessibility_divider"
         android:background="@null"/>
 
-</com.android.systemui.stackdivider.DividerView>
+</com.android.wm.shell.splitscreen.DividerView>
diff --git a/packages/SystemUI/res/layout/forced_resizable_activity.xml b/libs/WindowManager/Shell/res/layout/forced_resizable_activity.xml
similarity index 100%
rename from packages/SystemUI/res/layout/forced_resizable_activity.xml
rename to libs/WindowManager/Shell/res/layout/forced_resizable_activity.xml
diff --git a/packages/SystemUI/res/xml/one_handed_tutorial.xml b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
similarity index 100%
rename from packages/SystemUI/res/xml/one_handed_tutorial.xml
rename to libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
similarity index 100%
rename from libs/WindowManager/Shell/res/layout/pip_menu_activity.xml
rename to libs/WindowManager/Shell/res/layout/pip_menu.xml
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
new file mode 100644
index 0000000..7242793
--- /dev/null
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -0,0 +1,46 @@
+{
+  "version": "1.0.0",
+  "messages": {
+    "-1340279385": {
+      "message": "Remove listener=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "-880817403": {
+      "message": "Task vanished taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "-460572385": {
+      "message": "Task appeared taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "-242812822": {
+      "message": "Add listener for modes=%s listener=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "157713005": {
+      "message": "Task info changed taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "980952660": {
+      "message": "Task root back pressed taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    }
+  },
+  "groups": {
+    "WM_SHELL_TASK_ORG": {
+      "tag": "WindowManagerShell"
+    }
+  }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/libs/WindowManager/Shell/res/values-land/dimens.xml
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
copy to libs/WindowManager/Shell/res/values-land/dimens.xml
index 9d05843..77a601d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/libs/WindowManager/Shell/res/values-land/dimens.xml
@@ -1,24 +1,21 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2020, 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
+ *     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.settings
-
-import android.content.ContentResolver
-
-interface CurrentUserContentResolverProvider {
-
-    val currentUserContentResolver: ContentResolver
-}
\ No newline at end of file
+*/
+-->
+<resources>
+    <dimen name="docked_divider_handle_width">2dp</dimen>
+    <dimen name="docked_divider_handle_height">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml
new file mode 100644
index 0000000..863bb69
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-land/styles.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="DockedDividerBackground">
+        <item name="android:layout_width">10dp</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+
+    <style name="DockedDividerHandle">
+        <item name="android:layout_gravity">center_vertical</item>
+        <item name="android:layout_width">48dp</item>
+        <item name="android:layout_height">96dp</item>
+    </style>
+
+    <style name="DockedDividerMinimizedShadow">
+        <item name="android:layout_width">8dp</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
+</resources>
+
diff --git a/libs/WindowManager/Shell/res/values-sw600dp/config.xml b/libs/WindowManager/Shell/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..f194532f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sw600dp/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <!-- Animation duration when using long press on recents to dock -->
+    <integer name="long_press_dock_anim_duration">290</integer>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
new file mode 100644
index 0000000..6a19083
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2020, 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.
+ */
+-->
+<resources>
+    <color name="docked_divider_background">#ff000000</color>
+    <color name="docked_divider_handle">#ffffff</color>
+    <drawable name="forced_resizable_background">#59000000</drawable>
+    <color name="minimize_dock_shadow_start">#60000000</color>
+    <color name="minimize_dock_shadow_end">#00000000</color>
+</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 245c072..63b0f6f 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -26,4 +26,10 @@
 
     <!-- Allow PIP to enable round corner, see also R.dimen.pip_corner_radius -->
     <bool name="config_pipEnableRoundCorner">false</bool>
+
+    <!-- Animation duration when using long press on recents to dock -->
+    <integer name="long_press_dock_anim_duration">250</integer>
+
+    <!-- Allow one handed to enable round corner -->
+    <bool name="config_one_handed_enable_round_corner">true</bool>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1c12176..7fb641a 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -56,4 +56,14 @@
     <dimen name="pip_resize_handle_size">12dp</dimen>
     <dimen name="pip_resize_handle_margin">4dp</dimen>
     <dimen name="pip_resize_handle_padding">0dp</dimen>
+
+    <!-- How high we lift the divider when touching -->
+    <dimen name="docked_stack_divider_lift_elevation">4dp</dimen>
+
+    <dimen name="docked_divider_handle_width">16dp</dimen>
+    <dimen name="docked_divider_handle_height">2dp</dimen>
+
+    <!-- One-Handed Mode -->
+    <!-- Threshold for dragging distance to enable one-handed mode -->
+    <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
index ed20398..fb89238 100644
--- a/libs/WindowManager/Shell/res/values/ids.xml
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -16,4 +16,11 @@
 -->
 <resources>
     <item type="id" name="action_pip_resize" />
+
+    <!-- Accessibility actions for the docked stack divider -->
+    <item type="id" name="action_move_tl_full" />
+    <item type="id" name="action_move_tl_70" />
+    <item type="id" name="action_move_tl_50" />
+    <item type="id" name="action_move_tl_30" />
+    <item type="id" name="action_move_rb_full" />
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 6752b56..b6668fb 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -53,4 +53,44 @@
 
     <!-- TODO Deprecated. Label for PIP the drag to dismiss hint. DO NOT TRANSLATE [CHAR LIMIT=NONE]-->
     <string name="pip_phone_dismiss_hint">Drag down to dismiss</string>
+
+    <!-- Multi-Window strings -->
+    <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed in split-screen and that things might crash/not work properly [CHAR LIMIT=NONE] -->
+    <string name="dock_forced_resizable">App may not work with split-screen.</string>
+    <!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead. -->
+    <string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
+    <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] -->
+    <string name="forced_resizable_secondary_display">App may not work on a secondary display.</string>
+    <!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. -->
+    <string name="activity_launch_on_secondary_display_failed_text">App does not support launch on secondary displays.</string>
+
+    <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] -->
+    <string name="accessibility_divider">Split-screen divider</string>
+
+    <!-- Accessibility action for moving docked stack divider to make the left screen full screen [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_left_full">Left full screen</string>
+    <!-- Accessibility action for moving docked stack divider to make the left screen 70% [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_left_70">Left 70%</string>
+    <!-- Accessibility action for moving docked stack divider to make the left screen 50% [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_left_50">Left 50%</string>
+    <!-- Accessibility action for moving docked stack divider to make the left screen 30% [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_left_30">Left 30%</string>
+    <!-- Accessibility action for moving docked stack divider to make the right screen full screen [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_right_full">Right full screen</string>
+
+    <!-- Accessibility action for moving docked stack divider to make the top screen full screen [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_top_full">Top full screen</string>
+    <!-- Accessibility action for moving docked stack divider to make the top screen 70% [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_top_70">Top 70%</string>
+    <!-- Accessibility action for moving docked stack divider to make the top screen 50% [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_top_50">Top 50%</string>
+    <!-- Accessibility action for moving docked stack divider to make the top screen 30% [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_top_30">Top 30%</string>
+    <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] -->
+    <string name="accessibility_action_divider_bottom_full">Bottom full screen</string>
+
+    <!-- One-Handed Tutorial title [CHAR LIMIT=60] -->
+    <string name="one_handed_tutorial_title">Using one-handed mode</string>
+    <!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
+    <string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
new file mode 100644
index 0000000..fffcd33
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Theme used for the activity that shows when the system forced an app to be resizable -->
+    <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
+        <item name="android:windowBackground">@drawable/forced_resizable_background</item>
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
+    </style>
+
+    <style name="Animation.ForcedResizable" parent="@android:style/Animation">
+        <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
+
+        <!-- If the target stack doesn't have focus, we do a task to front animation. -->
+        <item name="android:taskToFrontEnterAnimation">@anim/forced_resizable_enter</item>
+        <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
+    </style>
+
+    <style name="DockedDividerBackground">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">10dp</item>
+        <item name="android:layout_gravity">center_vertical</item>
+    </style>
+
+    <style name="DockedDividerMinimizedShadow">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">8dp</item>
+    </style>
+
+    <style name="DockedDividerHandle">
+        <item name="android:layout_gravity">center_horizontal</item>
+        <item name="android:layout_width">96dp</item>
+        <item name="android:layout_height">48dp</item>
+    </style>
+</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 1263748..f9ba695c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -20,17 +20,20 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.Surface;
 import android.view.SurfaceControl;
+import android.window.ITaskOrganizerController;
 import android.window.TaskOrganizer;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
 import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Arrays;
 
 /**
  * Unified task organizer for all components in the shell.
+ * TODO(b/167582004): may consider consolidating this class and TaskOrganizer
  */
 public class ShellTaskOrganizer extends TaskOrganizer {
 
@@ -53,10 +56,21 @@
     // require us to report to both old and new listeners)
     private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
 
+    public ShellTaskOrganizer() {
+        super();
+    }
+
+    @VisibleForTesting
+    ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController) {
+        super(taskOrganizerController);
+    }
+
     /**
      * Adds a listener for tasks in a specific windowing mode.
      */
     public void addListener(TaskListener listener, int... windowingModes) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for modes=%s listener=%s",
+                Arrays.toString(windowingModes), listener);
         for (int winMode : windowingModes) {
             ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
             if (listeners == null) {
@@ -84,6 +98,7 @@
      * Removes a registered listener.
      */
     public void removeListener(TaskListener listener) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
         for (int i = 0; i < mListenersByWindowingMode.size(); i++) {
             mListenersByWindowingMode.valueAt(i).remove(listener);
         }
@@ -91,6 +106,8 @@
 
     @Override
     public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
+                taskInfo.taskId);
         mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
         ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
                 getWindowingMode(taskInfo));
@@ -103,6 +120,8 @@
 
     @Override
     public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
+                taskInfo.taskId);
         Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
         int winMode = getWindowingMode(taskInfo);
         int prevWinMode = getWindowingMode(data.first);
@@ -134,6 +153,8 @@
 
     @Override
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d",
+                taskInfo.taskId);
         ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
                 getWindowingMode(taskInfo));
         if (listeners != null) {
@@ -145,6 +166,8 @@
 
     @Override
     public void onTaskVanished(RunningTaskInfo taskInfo) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
+                taskInfo.taskId);
         int prevWinMode = getWindowingMode(mTasks.get(taskInfo.taskId).first);
         mTasks.remove(taskInfo.taskId);
         ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
similarity index 76%
rename from packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
index 12749fd..357f777 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
@@ -11,10 +11,10 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-package com.android.systemui.statusbar;
+package com.android.wm.shell.animation;
 
 import android.animation.Animator;
 import android.util.DisplayMetrics;
@@ -23,11 +23,6 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
-import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-
-import javax.inject.Inject;
-
 /**
  * Utility class to calculate general fling animation when the finger is released.
  */
@@ -63,9 +58,9 @@
 
     /**
      * @param maxLengthSeconds the longest duration an animation can become in seconds
-     * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
-     *                      the end of the animation. 0 means it's at the beginning and no
-     *                      acceleration will take place.
+     * @param speedUpFactor    a factor from 0 to 1 how much the slow down should be shifted towards
+     *                         the end of the animation. 0 means it's at the beginning and no
+     *                         acceleration will take place.
      */
     public FlingAnimationUtils(DisplayMetrics displayMetrics, float maxLengthSeconds,
             float speedUpFactor) {
@@ -74,19 +69,19 @@
 
     /**
      * @param maxLengthSeconds the longest duration an animation can become in seconds
-     * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
-     *                      the end of the animation. 0 means it's at the beginning and no
-     *                      acceleration will take place.
-     * @param x2 the x value to take for the second point of the bezier spline. If a value below 0
-     *           is provided, the value is automatically calculated.
-     * @param y2 the y value to take for the second point of the bezier spline
+     * @param speedUpFactor    a factor from 0 to 1 how much the slow down should be shifted towards
+     *                         the end of the animation. 0 means it's at the beginning and no
+     *                         acceleration will take place.
+     * @param x2               the x value to take for the second point of the bezier spline. If a
+     *                         value below 0 is provided, the value is automatically calculated.
+     * @param y2               the y value to take for the second point of the bezier spline
      */
     public FlingAnimationUtils(DisplayMetrics displayMetrics, float maxLengthSeconds,
             float speedUpFactor, float x2, float y2) {
         mMaxLengthSeconds = maxLengthSeconds;
         mSpeedUpFactor = speedUpFactor;
         if (x2 < 0) {
-            mLinearOutSlowInX2 = NotificationUtils.interpolate(LINEAR_OUT_SLOW_IN_X2,
+            mLinearOutSlowInX2 = interpolate(LINEAR_OUT_SLOW_IN_X2,
                     LINEAR_OUT_SLOW_IN_X2_MAX,
                     mSpeedUpFactor);
         } else {
@@ -102,10 +97,10 @@
      * Applies the interpolator and length to the animator, such that the fling animation is
      * consistent with the finger motion.
      *
-     * @param animator the animator to apply
+     * @param animator  the animator to apply
      * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
+     * @param endValue  the end value of the animator
+     * @param velocity  the current velocity of the motion
      */
     public void apply(Animator animator, float currValue, float endValue, float velocity) {
         apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
@@ -115,10 +110,10 @@
      * Applies the interpolator and length to the animator, such that the fling animation is
      * consistent with the finger motion.
      *
-     * @param animator the animator to apply
+     * @param animator  the animator to apply
      * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
+     * @param endValue  the end value of the animator
+     * @param velocity  the current velocity of the motion
      */
     public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
             float velocity) {
@@ -129,10 +124,10 @@
      * Applies the interpolator and length to the animator, such that the fling animation is
      * consistent with the finger motion.
      *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
+     * @param animator    the animator to apply
+     * @param currValue   the current value
+     * @param endValue    the end value of the animator
+     * @param velocity    the current velocity of the motion
      * @param maxDistance the maximum distance for this interaction; the maximum animation length
      *                    gets multiplied by the ratio between the actual distance and this value
      */
@@ -140,18 +135,18 @@
             float maxDistance) {
         AnimatorProperties properties = getProperties(currValue, endValue, velocity,
                 maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
+        animator.setDuration(properties.mDuration);
+        animator.setInterpolator(properties.mInterpolator);
     }
 
     /**
      * Applies the interpolator and length to the animator, such that the fling animation is
      * consistent with the finger motion.
      *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
+     * @param animator    the animator to apply
+     * @param currValue   the current value
+     * @param endValue    the end value of the animator
+     * @param velocity    the current velocity of the motion
      * @param maxDistance the maximum distance for this interaction; the maximum animation length
      *                    gets multiplied by the ratio between the actual distance and this value
      */
@@ -159,8 +154,8 @@
             float velocity, float maxDistance) {
         AnimatorProperties properties = getProperties(currValue, endValue, velocity,
                 maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
+        animator.setDuration(properties.mDuration);
+        animator.setInterpolator(properties.mInterpolator);
     }
 
     private AnimatorProperties getProperties(float currValue,
@@ -171,28 +166,28 @@
         float velAbs = Math.abs(velocity);
         float velocityFactor = mSpeedUpFactor == 0.0f
                 ? 1.0f : Math.min(velAbs / HIGH_VELOCITY_DP_PER_SECOND, 1.0f);
-        float startGradient = NotificationUtils.interpolate(LINEAR_OUT_SLOW_IN_START_GRADIENT,
+        float startGradient = interpolate(LINEAR_OUT_SLOW_IN_START_GRADIENT,
                 mY2 / mLinearOutSlowInX2, velocityFactor);
         float durationSeconds = startGradient * diff / velAbs;
         Interpolator slowInInterpolator = getInterpolator(startGradient, velocityFactor);
         if (durationSeconds <= maxLengthSeconds) {
-            mAnimatorProperties.interpolator = slowInInterpolator;
+            mAnimatorProperties.mInterpolator = slowInInterpolator;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
             durationSeconds = maxLengthSeconds;
-            VelocityInterpolator velocityInterpolator
-                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
+            VelocityInterpolator velocityInterpolator = new VelocityInterpolator(
+                    durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, slowInInterpolator, Interpolators.LINEAR_OUT_SLOW_IN);
-            mAnimatorProperties.interpolator = superInterpolator;
+            mAnimatorProperties.mInterpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
+            mAnimatorProperties.mInterpolator = Interpolators.FAST_OUT_SLOW_IN;
         }
-        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        mAnimatorProperties.mDuration = (long) (durationSeconds * 1000);
         return mAnimatorProperties;
     }
 
@@ -225,10 +220,10 @@
      * consistent with the finger motion for the case when the animation is making something
      * disappear.
      *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
+     * @param animator    the animator to apply
+     * @param currValue   the current value
+     * @param endValue    the end value of the animator
+     * @param velocity    the current velocity of the motion
      * @param maxDistance the maximum distance for this interaction; the maximum animation length
      *                    gets multiplied by the ratio between the actual distance and this value
      */
@@ -236,8 +231,8 @@
             float velocity, float maxDistance) {
         AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
                 maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
+        animator.setDuration(properties.mDuration);
+        animator.setInterpolator(properties.mInterpolator);
     }
 
     /**
@@ -245,10 +240,10 @@
      * consistent with the finger motion for the case when the animation is making something
      * disappear.
      *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
+     * @param animator    the animator to apply
+     * @param currValue   the current value
+     * @param endValue    the end value of the animator
+     * @param velocity    the current velocity of the motion
      * @param maxDistance the maximum distance for this interaction; the maximum animation length
      *                    gets multiplied by the ratio between the actual distance and this value
      */
@@ -256,8 +251,8 @@
             float velocity, float maxDistance) {
         AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
                 maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
+        animator.setDuration(properties.mDuration);
+        animator.setInterpolator(properties.mInterpolator);
     }
 
     private AnimatorProperties getDismissingProperties(float currValue, float endValue,
@@ -272,24 +267,24 @@
         Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2);
         float durationSeconds = startGradient * diff / velAbs;
         if (durationSeconds <= maxLengthSeconds) {
-            mAnimatorProperties.interpolator = mLinearOutFasterIn;
+            mAnimatorProperties.mInterpolator = mLinearOutFasterIn;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between linear-out-faster-in and linear interpolator with current
             // velocity.
             durationSeconds = maxLengthSeconds;
-            VelocityInterpolator velocityInterpolator
-                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
+            VelocityInterpolator velocityInterpolator = new VelocityInterpolator(
+                    durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, mLinearOutFasterIn, Interpolators.LINEAR_OUT_SLOW_IN);
-            mAnimatorProperties.interpolator = superInterpolator;
+            mAnimatorProperties.mInterpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
+            mAnimatorProperties.mInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
         }
-        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        mAnimatorProperties.mDuration = (long) (durationSeconds * 1000);
         return mAnimatorProperties;
     }
 
@@ -361,10 +356,11 @@
     }
 
     private static class AnimatorProperties {
-        Interpolator interpolator;
-        long duration;
+        Interpolator mInterpolator;
+        long mDuration;
     }
 
+    /** Builder for {@link #FlingAnimationUtils}. */
     public static class Builder {
         private final DisplayMetrics mDisplayMetrics;
         float mMaxLengthSeconds;
@@ -372,32 +368,39 @@
         float mX2;
         float mY2;
 
-        @Inject
         public Builder(DisplayMetrics displayMetrics) {
             mDisplayMetrics = displayMetrics;
             reset();
         }
 
+        /** Sets the longest duration an animation can become in seconds. */
         public Builder setMaxLengthSeconds(float maxLengthSeconds) {
             mMaxLengthSeconds = maxLengthSeconds;
             return this;
         }
 
+        /**
+         * Sets the factor for how much the slow down should be shifted towards the end of the
+         * animation.
+         */
         public Builder setSpeedUpFactor(float speedUpFactor) {
             mSpeedUpFactor = speedUpFactor;
             return this;
         }
 
+        /** Sets the x value to take for the second point of the bezier spline. */
         public Builder setX2(float x2) {
             mX2 = x2;
             return this;
         }
 
+        /** Sets the y value to take for the second point of the bezier spline. */
         public Builder setY2(float y2) {
             mY2 = y2;
             return this;
         }
 
+        /** Resets all parameters of the builder. */
         public Builder reset() {
             mMaxLengthSeconds = 0;
             mSpeedUpFactor = 0.0f;
@@ -407,9 +410,14 @@
             return this;
         }
 
+        /** Builds {@link #FlingAnimationUtils}. */
         public FlingAnimationUtils build() {
             return new FlingAnimationUtils(mDisplayMetrics, mMaxLengthSeconds, mSpeedUpFactor,
                     mX2, mY2);
         }
     }
+
+    private static float interpolate(float start, float end, float amount) {
+        return start * (1.0f - amount) + end * amount;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
new file mode 100644
index 0000000..b794b91
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.animation;
+
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Common interpolators used in wm shell library.
+ */
+public class Interpolators {
+    /**
+     * Interpolator for fast out linear in animation.
+     */
+    public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+    /**
+     * Interpolator for fast out slow in animation.
+     */
+    public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+    /**
+     * Interpolator for linear out slow in animation.
+     */
+    public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+
+    /**
+     * Interpolator to be used when animating a move based on a click. Pair with enough duration.
+     */
+    public static final Interpolator TOUCH_RESPONSE = new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index 13ed02e..9cb1250 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.common;
 
+import android.annotation.NonNull;
 import android.os.Handler;
 import android.util.Slog;
 import android.view.SurfaceControl;
@@ -23,17 +24,13 @@
 import android.window.WindowContainerTransactionCallback;
 import android.window.WindowOrganizer;
 
-import androidx.annotation.NonNull;
-
-import com.android.wm.shell.common.TransactionPool;
-
 import java.util.ArrayList;
 
 /**
  * Helper for serializing sync-transactions and corresponding callbacks.
  */
-class SyncTransactionQueue {
-    private static final boolean DEBUG = SplitScreenController.DEBUG;
+public final class SyncTransactionQueue {
+    private static final boolean DEBUG = false;
     private static final String TAG = "SyncTransactionQueue";
 
     // Just a little longer than the sync-engine timeout of 5s
@@ -58,7 +55,7 @@
         }
     };
 
-    SyncTransactionQueue(TransactionPool pool, Handler handler) {
+    public SyncTransactionQueue(TransactionPool pool, Handler handler) {
         mTransactionPool = pool;
         mHandler = handler;
     }
@@ -66,7 +63,7 @@
     /**
      * Queues a sync transaction to be sent serially to WM.
      */
-    void queue(WindowContainerTransaction wct) {
+    public void queue(WindowContainerTransaction wct) {
         SyncCallback cb = new SyncCallback(wct);
         synchronized (mQueue) {
             if (DEBUG) Slog.d(TAG, "Queueing up " + wct);
@@ -82,7 +79,7 @@
      * Otherwise just returns without queueing.
      * @return {@code true} if queued, {@code false} if not.
      */
-    boolean queueIfWaiting(WindowContainerTransaction wct) {
+    public boolean queueIfWaiting(WindowContainerTransaction wct) {
         synchronized (mQueue) {
             if (mQueue.isEmpty()) {
                 if (DEBUG) Slog.d(TAG, "Nothing in queue, so skip queueing up " + wct);
@@ -102,7 +99,7 @@
      * Runs a runnable in sync with sync transactions (ie. when the current in-flight transaction
      * returns. If there are no transactions in-flight, runnable executes immediately.
      */
-    void runInSync(TransactionRunnable runnable) {
+    public void runInSync(TransactionRunnable runnable) {
         synchronized (mQueue) {
             if (DEBUG) Slog.d(TAG, "Run in sync. mInFlight=" + mInFlight);
             if (mInFlight != null) {
@@ -127,7 +124,9 @@
         t.close();
     }
 
-    interface TransactionRunnable {
+    /** Task to run with transaction. */
+    public interface TransactionRunnable {
+        /** Runs with transaction. */
         void runWithTransaction(SurfaceControl.Transaction t);
     }
 
@@ -154,7 +153,7 @@
 
         @Override
         public void onTransactionReady(int id,
-                @androidx.annotation.NonNull SurfaceControl.Transaction t) {
+                @NonNull SurfaceControl.Transaction t) {
             mHandler.post(() -> {
                 synchronized (mQueue) {
                     if (mId != id) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
new file mode 100644
index 0000000..9c78fc5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.onehanded;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
+
+import java.io.PrintWriter;
+
+/**
+ * Interface to engage one handed feature.
+ */
+public interface OneHanded {
+    /**
+     * Return whether the device has one handed feature or not.
+     */
+    boolean hasOneHandedFeature();
+
+    /**
+     * Return one handed settings enabled or not.
+     */
+    boolean isOneHandedEnabled();
+
+    /**
+     * Return swipe to notification settings enabled or not.
+     */
+    boolean isSwipeToNotificationEnabled();
+
+    /**
+     * Enters one handed mode.
+     */
+    void startOneHanded();
+
+    /**
+     * Exits one handed mode.
+     */
+    void stopOneHanded();
+
+    /**
+     * Exits one handed mode with {@link OneHandedEvents}.
+     */
+    void stopOneHanded(int event);
+
+    /**
+     * Set navigation 3 button mode enabled or disabled by users.
+     */
+    void setThreeButtonModeEnabled(boolean enabled);
+
+    /**
+     * Register callback to be notified after {@link OneHandedDisplayAreaOrganizer}
+     * transition start or finish
+     */
+    void registerTransitionCallback(OneHandedTransitionCallback callback);
+
+    /**
+     * Register callback for one handed gesture, this gesture callbcak will be activated on
+     * 3 button navigation mode only
+     */
+    void registerGestureCallback(OneHandedGestureEventCallback callback);
+
+    /**
+     * Dump one handed status.
+     */
+    void dump(@NonNull PrintWriter pw);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
index 264ace7..6749f7e 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.view.SurfaceControl;
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 9be1b5a..9639096 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
new file mode 100644
index 0000000..c84b478
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.onehanded;
+
+import static android.os.UserHandle.USER_CURRENT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.content.Context;
+import android.content.om.IOverlayManager;
+import android.content.om.OverlayInfo;
+import android.database.ContentObserver;
+import android.graphics.Point;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages and manipulates the one handed states, transitions, and gesture for phones.
+ */
+public class OneHandedController implements OneHanded {
+    private static final String TAG = "OneHandedController";
+
+    private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
+            "persist.debug.one_handed_offset_percentage";
+    private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
+            "com.android.internal.systemui.onehanded.gestural";
+
+    static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+
+    private final boolean mHasOneHandedFeature;
+    private boolean mIsOneHandedEnabled;
+    private boolean mIsSwipeToNotificationEnabled;
+    private boolean mTaskChangeToExit;
+    private float mOffSetFraction;
+
+    private final Context mContext;
+    private final DisplayController mDisplayController;
+    private final OneHandedGestureHandler mGestureHandler;
+    private final OneHandedTimeoutHandler mTimeoutHandler;
+    private final OneHandedTouchHandler mTouchHandler;
+    private final OneHandedTutorialHandler mTutorialHandler;
+    private final IOverlayManager mOverlayManager;
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+    private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
+
+    /**
+     * Handle rotation based on OnDisplayChangingListener callback
+     */
+    private final DisplayChangeController.OnDisplayChangingListener mRotationController =
+            (display, fromRotation, toRotation, wct) -> {
+                if (mDisplayAreaOrganizer != null) {
+                    mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
+                }
+            };
+
+    private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) {
+        @Override
+        public void onChange(boolean selfChange) {
+            final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+                    mContext.getContentResolver());
+            OneHandedEvents.writeEvent(enabled
+                    ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
+                    : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
+
+            setOneHandedEnabled(enabled);
+
+            // Also checks swipe to notification settings since they all need gesture overlay.
+            setEnabledGesturalOverlay(
+                    enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                            mContext.getContentResolver()));
+        }
+    };
+
+    private final ContentObserver mTimeoutObserver = new ContentObserver(mMainHandler) {
+        @Override
+        public void onChange(boolean selfChange) {
+            final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
+                    mContext.getContentResolver());
+            int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId();
+            switch (newTimeout) {
+                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
+                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER;
+                    break;
+                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS:
+                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4;
+                    break;
+                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS:
+                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8;
+                    break;
+                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS:
+                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12;
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+            OneHandedEvents.writeEvent(metricsId);
+
+            if (mTimeoutHandler != null) {
+                mTimeoutHandler.setTimeout(newTimeout);
+            }
+        }
+    };
+
+    private final ContentObserver mTaskChangeExitObserver = new ContentObserver(mMainHandler) {
+        @Override
+        public void onChange(boolean selfChange) {
+            final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
+                    mContext.getContentResolver());
+            OneHandedEvents.writeEvent(enabled
+                    ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
+                    : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
+
+            setTaskChangeToExit(enabled);
+        }
+    };
+
+    private final ContentObserver mSwipeToNotificationEnabledObserver =
+            new ContentObserver(mMainHandler) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    final boolean enabled =
+                            OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                                    mContext.getContentResolver());
+                    setSwipeToNotificationEnabled(enabled);
+
+                    // Also checks one handed mode settings since they all need gesture overlay.
+                    setEnabledGesturalOverlay(
+                            enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+                                    mContext.getContentResolver()));
+                }
+            };
+
+    /**
+     * The static constructor method to create OneHnadedController.
+     */
+    public static OneHandedController create(
+            Context context, DisplayController displayController) {
+        OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context);
+        OneHandedAnimationController animationController =
+                new OneHandedAnimationController(context);
+        OneHandedTouchHandler touchHandler = new OneHandedTouchHandler();
+        OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
+                context, displayController);
+        OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
+                context, displayController, animationController, tutorialHandler);
+        return new OneHandedController(context, displayController, organizer, touchHandler,
+                tutorialHandler, gestureHandler);
+    }
+
+    @VisibleForTesting
+    OneHandedController(Context context,
+            DisplayController displayController,
+            OneHandedDisplayAreaOrganizer displayAreaOrganizer,
+            OneHandedTouchHandler touchHandler,
+            OneHandedTutorialHandler tutorialHandler,
+            OneHandedGestureHandler gestureHandler) {
+        mHasOneHandedFeature = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
+        if (!mHasOneHandedFeature) {
+            Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
+            mContext = null;
+            mDisplayAreaOrganizer = null;
+            mDisplayController = null;
+            mTouchHandler = null;
+            mTutorialHandler = null;
+            mGestureHandler = null;
+            mTimeoutHandler = null;
+            mOverlayManager = null;
+            return;
+        }
+
+        mContext = context;
+        mDisplayAreaOrganizer = displayAreaOrganizer;
+        mDisplayController = displayController;
+        mTouchHandler = touchHandler;
+        mTutorialHandler = tutorialHandler;
+        mGestureHandler = gestureHandler;
+
+        mOverlayManager = IOverlayManager.Stub.asInterface(
+                ServiceManager.getService(Context.OVERLAY_SERVICE));
+        mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f;
+        mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+                context.getContentResolver());
+        mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+                context.getContentResolver());
+        mTimeoutHandler = OneHandedTimeoutHandler.get();
+
+        mDisplayController.addDisplayChangingController(mRotationController);
+
+        setupCallback();
+        setupSettingObservers();
+        setupTimeoutListener();
+        setupGesturalOverlay();
+        updateSettings();
+    }
+
+    /**
+     * Set one handed enabled or disabled when user update settings
+     */
+    void setOneHandedEnabled(boolean enabled) {
+        mIsOneHandedEnabled = enabled;
+        updateOneHandedEnabled();
+    }
+
+    /**
+     * Set one handed enabled or disabled by when user update settings
+     */
+    void setTaskChangeToExit(boolean enabled) {
+        mTaskChangeToExit = enabled;
+    }
+
+    /**
+     * Sets whether to enable swipe bottom to notification gesture when user update settings.
+     */
+    void setSwipeToNotificationEnabled(boolean enabled) {
+        mIsSwipeToNotificationEnabled = enabled;
+        updateOneHandedEnabled();
+    }
+
+    @Override
+    public boolean hasOneHandedFeature() {
+        return mHasOneHandedFeature;
+    }
+
+    @Override
+    public boolean isOneHandedEnabled() {
+        return mIsOneHandedEnabled;
+    }
+
+    @Override
+    public boolean isSwipeToNotificationEnabled() {
+        return mIsSwipeToNotificationEnabled;
+    }
+
+    @Override
+    public void startOneHanded() {
+        if (!mDisplayAreaOrganizer.isInOneHanded()) {
+            final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
+            mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
+            mTimeoutHandler.resetTimer();
+
+            OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
+        }
+    }
+
+    @Override
+    public void stopOneHanded() {
+        if (mDisplayAreaOrganizer.isInOneHanded()) {
+            mDisplayAreaOrganizer.scheduleOffset(0, 0);
+            mTimeoutHandler.removeTimer();
+        }
+    }
+
+    @Override
+    public void stopOneHanded(int event) {
+        if (!mTaskChangeToExit && event == OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT) {
+            //Task change exit not enable, do nothing and return here.
+            return;
+        }
+
+        if (mDisplayAreaOrganizer.isInOneHanded()) {
+            OneHandedEvents.writeEvent(event);
+        }
+
+        stopOneHanded();
+    }
+
+    @Override
+    public void setThreeButtonModeEnabled(boolean enabled) {
+        mGestureHandler.onThreeButtonModeEnabled(enabled);
+    }
+
+    @Override
+    public void registerTransitionCallback(OneHandedTransitionCallback callback) {
+        mDisplayAreaOrganizer.registerTransitionCallback(callback);
+    }
+
+    @Override
+    public void registerGestureCallback(OneHandedGestureEventCallback callback) {
+        mGestureHandler.setGestureEventListener(callback);
+    }
+
+    private void setupCallback() {
+        mTouchHandler.registerTouchEventListener(() ->
+                stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT));
+        mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
+        mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
+        mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
+    }
+
+    private void setupSettingObservers() {
+        OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
+                mContext.getContentResolver(), mEnabledObserver);
+        OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
+                mContext.getContentResolver(), mTimeoutObserver);
+        OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT,
+                mContext.getContentResolver(), mTaskChangeExitObserver);
+        OneHandedSettingsUtil.registerSettingsKeyObserver(
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+                mContext.getContentResolver(), mSwipeToNotificationEnabledObserver);
+    }
+
+    private void updateSettings() {
+        setOneHandedEnabled(OneHandedSettingsUtil
+                .getSettingsOneHandedModeEnabled(mContext.getContentResolver()));
+        mTimeoutHandler.setTimeout(OneHandedSettingsUtil
+                .getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
+        setTaskChangeToExit(OneHandedSettingsUtil
+                .getSettingsTapsAppToExit(mContext.getContentResolver()));
+        setSwipeToNotificationEnabled(OneHandedSettingsUtil
+                .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
+    }
+
+    private void setupTimeoutListener() {
+        mTimeoutHandler.registerTimeoutListener(timeoutTime -> {
+            stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT);
+        });
+    }
+
+    /**
+     * Query the current display real size from {@link DisplayController}
+     *
+     * @return {@link DisplayController#getDisplay(int)#getDisplaySize()}
+     */
+    private Point getDisplaySize() {
+        Point displaySize = new Point();
+        if (mDisplayController != null && mDisplayController.getDisplay(DEFAULT_DISPLAY) != null) {
+            mDisplayController.getDisplay(DEFAULT_DISPLAY).getRealSize(displaySize);
+        }
+        return displaySize;
+    }
+
+    private void updateOneHandedEnabled() {
+        if (mDisplayAreaOrganizer.isInOneHanded()) {
+            stopOneHanded();
+        }
+        // TODO Be aware to unregisterOrganizer() after animation finished
+        mDisplayAreaOrganizer.unregisterOrganizer();
+        if (mIsOneHandedEnabled) {
+            mDisplayAreaOrganizer.registerOrganizer(
+                    OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
+        }
+        mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
+        mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
+    }
+
+    private void setupGesturalOverlay() {
+        if (!OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver())) {
+            return;
+        }
+
+        OverlayInfo info = null;
+        try {
+            // TODO(b/157958539) migrate new RRO config file after S+
+            mOverlayManager.setHighestPriority(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
+            info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
+        } catch (RemoteException e) { /* Do nothing */ }
+
+        if (info != null && !info.isEnabled()) {
+            // Enable the default gestural one handed overlay.
+            setEnabledGesturalOverlay(true);
+        }
+    }
+
+    @VisibleForTesting
+    private void setEnabledGesturalOverlay(boolean enabled) {
+        try {
+            mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public void dump(@NonNull PrintWriter pw) {
+        final String innerPrefix = "  ";
+        pw.println(TAG + "states: ");
+        pw.print(innerPrefix + "mOffSetFraction=");
+        pw.println(mOffSetFraction);
+
+        if (mDisplayAreaOrganizer != null) {
+            mDisplayAreaOrganizer.dump(pw);
+        }
+
+        if (mTouchHandler != null) {
+            mTouchHandler.dump(pw);
+        }
+
+        if (mTimeoutHandler != null) {
+            mTimeoutHandler.dump(pw);
+        }
+
+        if (mTutorialHandler != null) {
+            mTutorialHandler.dump(pw);
+        }
+
+        OneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver());
+
+        if (mOverlayManager != null) {
+            OverlayInfo info = null;
+            try {
+                info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY,
+                        USER_CURRENT);
+            } catch (RemoteException e) { /* Do nothing */ }
+
+            if (info != null && !info.isEnabled()) {
+                pw.print(innerPrefix + "OverlayInfo=");
+                pw.println(info);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index ad9f7ea..9954618 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
-import static com.android.systemui.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
+import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
+import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
 
 import android.content.Context;
 import android.graphics.Point;
@@ -38,10 +38,8 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.os.SomeArgs;
-import com.android.systemui.Dumpable;
 import com.android.wm.shell.common.DisplayController;
 
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -58,7 +56,7 @@
  *
  * This class is also responsible for translating one handed operations within SysUI component
  */
-public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer implements Dumpable {
+public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
     private static final String TAG = "OneHandedDisplayAreaOrganizer";
     private static final String ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION =
             "persist.debug.one_handed_translate_animation_duration";
@@ -353,8 +351,7 @@
         return args;
     }
 
-    @Override
-    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+    void dump(@NonNull PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG + "states: ");
         pw.print(innerPrefix + "mIsInOneHanded=");
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedEvents.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedEvents.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java
index 327ed67..79ddd2b 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedEvents.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEvents.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index f3be699..3b1e6cb 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -39,8 +38,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.R;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 
@@ -49,7 +47,6 @@
  * others(e.g, 2-button, full gesture mode) are handled by Launcher quick steps.
  */
 public class OneHandedGestureHandler implements OneHandedTransitionCallback,
-        NavigationModeController.ModeChangedListener,
         DisplayChangeController.OnDisplayChangingListener {
     private static final String TAG = "OneHandedGestureHandler";
     private static final boolean DEBUG_GESTURE = false;
@@ -66,7 +63,7 @@
     private boolean mAllowGesture;
     private boolean mIsEnabled;
     private int mNavGestureHeight;
-    private boolean mIsThreeButtonModeEnable;
+    private boolean mIsThreeButtonModeEnabled;
     private int mRotation = Surface.ROTATION_0;
 
     @VisibleForTesting
@@ -85,14 +82,10 @@
      *
      * @param context                  {@link Context}
      * @param displayController        {@link DisplayController}
-     * @param navigationModeController {@link NavigationModeController}
      */
-    public OneHandedGestureHandler(Context context, DisplayController displayController,
-            NavigationModeController navigationModeController) {
+    public OneHandedGestureHandler(Context context, DisplayController displayController) {
         mDisplayController = displayController;
         displayController.addDisplayChangingController(this);
-        final int NavBarMode = navigationModeController.addListener(this);
-        mIsThreeButtonModeEnable = (NavBarMode == NAV_BAR_MODE_3BUTTON);
         mNavGestureHeight = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.navigation_bar_gesture_height);
         mDragDistThreshold = context.getResources().getDimensionPixelSize(
@@ -115,6 +108,11 @@
         updateIsEnabled();
     }
 
+    void onThreeButtonModeEnabled(boolean isEnabled) {
+        mIsThreeButtonModeEnabled = isEnabled;
+        updateIsEnabled();
+    }
+
     /**
      * Register {@link OneHandedGestureEventCallback} to receive onStart(), onStop() callback
      */
@@ -199,7 +197,7 @@
     private void updateIsEnabled() {
         disposeInputChannel();
 
-        if (mIsEnabled && mIsThreeButtonModeEnable) {
+        if (mIsEnabled && mIsThreeButtonModeEnabled) {
             final Point displaySize = new Point();
             if (mDisplayController != null) {
                 final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
@@ -212,7 +210,7 @@
                     displaySize.y);
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "onehanded-gesture-offset", DEFAULT_DISPLAY);
-            mInputEventReceiver = new SysUiInputEventReceiver(
+            mInputEventReceiver = new EventReceiver(
                     mInputMonitor.getInputChannel(), Looper.getMainLooper());
         }
     }
@@ -224,22 +222,13 @@
     }
 
     @Override
-    public void onNavigationModeChanged(int mode) {
-        if (DEBUG_GESTURE) {
-            Log.d(TAG, "onNavigationModeChanged, mode =" + mode);
-        }
-        mIsThreeButtonModeEnable = (mode == NAV_BAR_MODE_3BUTTON);
-        updateIsEnabled();
-    }
-
-    @Override
     public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
             WindowContainerTransaction t) {
         mRotation = toRotation;
     }
 
-    private class SysUiInputEventReceiver extends InputEventReceiver {
-        SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+    private class EventReceiver extends InputEventReceiver {
+        EventReceiver(InputChannel channel, Looper looper) {
             super(channel, looper);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 0598f32..4d66f29 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.annotation.IntDef;
 import android.content.ContentResolver;
@@ -22,8 +22,6 @@
 import android.net.Uri;
 import android.provider.Settings;
 
-import com.android.systemui.dagger.SysUISingleton;
-
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -31,7 +29,6 @@
 /**
  * APIs for querying or updating one handed settings .
  */
-@SysUISingleton
 public final class OneHandedSettingsUtil {
     private static final String TAG = "OneHandedSettingsUtil";
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
index bc4a9b4..e7010db 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 
 /**
  * Abstracts the common operations on {@link SurfaceControl.Transaction} for OneHanded transition.
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedThread.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
index b7b814a..24d33ed 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedThread.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.os.Handler;
 import android.os.HandlerThread;
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
index 6bed304..9c97cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTimeoutHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
 
 import android.os.Handler;
 import android.os.Looper;
@@ -25,9 +25,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.Dumpable;
-
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -36,7 +33,7 @@
 /**
  * Timeout handler for stop one handed mode operations.
  */
-public class OneHandedTimeoutHandler implements Dumpable {
+public class OneHandedTimeoutHandler {
     private static final String TAG = "OneHandedTimeoutHandler";
     private static boolean sIsDragging = false;
     // Default timeout is ONE_HANDED_TIMEOUT_MEDIUM
@@ -150,8 +147,7 @@
         }
     }
 
-    @Override
-    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+    void dump(@NonNull PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG + "states: ");
         pw.print(innerPrefix + "sTimeout=");
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
index 8265da6..721382d 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -30,9 +30,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.Dumpable;
-
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -40,7 +37,7 @@
  * to exit, reset timer when user is in one-handed mode.
  * Refer {@link OneHandedGestureHandler} to see start and stop one handed gesture
  */
-public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpable {
+public class OneHandedTouchHandler implements OneHandedTransitionCallback {
     private static final String TAG = "OneHandedTouchHandler";
     private final Rect mLastUpdatedBounds = new Rect();
 
@@ -130,7 +127,7 @@
         if (mIsEnabled) {
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "onehanded-touch", DEFAULT_DISPLAY);
-            mInputEventReceiver = new SysUiInputEventReceiver(
+            mInputEventReceiver = new EventReceiver(
                     mInputMonitor.getInputChannel(), Looper.getMainLooper());
         }
     }
@@ -146,16 +143,15 @@
         mIsOnStopTransitioning = false;
     }
 
-    @Override
-    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+    void dump(@NonNull PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG + "states: ");
         pw.print(innerPrefix + "mLastUpdatedBounds=");
         pw.println(mLastUpdatedBounds);
     }
 
-    private class SysUiInputEventReceiver extends InputEventReceiver {
-        SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+    private class EventReceiver extends InputEventReceiver {
+        EventReceiver(InputChannel channel, Looper looper) {
             super(channel, looper);
         }
 
@@ -170,11 +166,6 @@
      */
     public interface OneHandedTouchEventCallback {
         /**
-         * Handle the start event.
-         */
-        void onStart();
-
-        /**
          * Handle the exit event.
          */
         void onStop();
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTransitionCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTransitionCallback.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
index 75eb0eb..3af7c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTransitionCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.graphics.Rect;
 
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 0354c72..b15b515 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -33,10 +33,8 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.Dumpable;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -45,7 +43,7 @@
  * Refer {@link OneHandedGestureHandler} and {@link OneHandedTouchHandler} to see start and stop
  * one handed gesture
  */
-public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Dumpable {
+public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
     private static final String TAG = "OneHandedTutorialHandler";
     private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
             "persist.debug.one_handed_offset_percentage";
@@ -81,7 +79,7 @@
         mTargetViewContainer.setClipChildren(false);
         mTutorialAreaHeight = Math.round(mDisplaySize.y
                 * (SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f));
-        mTutorialView = LayoutInflater.from(context).inflate(R.xml.one_handed_tutorial, null);
+        mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
         mTargetViewContainer.addView(mTutorialView);
         mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
                 Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
@@ -158,7 +156,9 @@
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 mDisplaySize.x, mTutorialAreaHeight, 0, 0,
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSLUCENT);
         lp.gravity = Gravity.TOP | Gravity.LEFT;
         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
@@ -168,8 +168,7 @@
         return lp;
     }
 
-    @Override
-    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+    void dump(@NonNull PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG + "states: ");
         pw.print(innerPrefix + "mLastUpdatedBounds=");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
new file mode 100644
index 0000000..ae09754
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.protolog;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+/**
+ * Defines logging groups for ProtoLog.
+ *
+ * This file is used by the ProtoLogTool to generate optimized logging code.
+ */
+public enum ShellProtoLogGroup implements IProtoLogGroup {
+    WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM_SHELL),
+    TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
+
+    private final boolean mEnabled;
+    private volatile boolean mLogToProto;
+    private volatile boolean mLogToLogcat;
+    private final String mTag;
+
+    /**
+     * @param enabled     set to false to exclude all log statements for this group from
+     *                    compilation,
+     *                    they will not be available in runtime.
+     * @param logToProto  enable binary logging for the group
+     * @param logToLogcat enable text logging for the group
+     * @param tag         name of the source of the logged message
+     */
+    ShellProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+        this.mEnabled = enabled;
+        this.mLogToProto = logToProto;
+        this.mLogToLogcat = logToLogcat;
+        this.mTag = tag;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    @Override
+    public boolean isLogToProto() {
+        return mLogToProto;
+    }
+
+    @Override
+    public boolean isLogToLogcat() {
+        return mLogToLogcat;
+    }
+
+    @Override
+    public boolean isLogToAny() {
+        return mLogToLogcat || mLogToProto;
+    }
+
+    @Override
+    public String getTag() {
+        return mTag;
+    }
+
+    @Override
+    public void setLogToProto(boolean logToProto) {
+        this.mLogToProto = logToProto;
+    }
+
+    @Override
+    public void setLogToLogcat(boolean logToLogcat) {
+        this.mLogToLogcat = logToLogcat;
+    }
+
+    private static class Consts {
+        private static final String TAG_WM_SHELL = "WindowManagerShell";
+
+        private static final boolean ENABLE_DEBUG = true;
+        private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
new file mode 100644
index 0000000..6a925e7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.protolog;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.BaseProtoLogImpl;
+import com.android.internal.protolog.ProtoLogViewerConfigReader;
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.wm.shell.R;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.json.JSONException;
+
+
+/**
+ * A service for the ProtoLog logging system.
+ */
+public class ShellProtoLogImpl extends BaseProtoLogImpl {
+    private static final String TAG = "ProtoLogImpl";
+    private static final int BUFFER_CAPACITY = 1024 * 1024;
+    // TODO: Get the right path for the proto log file when we initialize the shell components
+    private static final String LOG_FILENAME = new File("wm_shell_log.pb").getAbsolutePath();
+
+    private static ShellProtoLogImpl sServiceInstance = null;
+
+    private final PrintWriter mSystemOutWriter;
+
+    static {
+        addLogGroupEnum(ShellProtoLogGroup.values());
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void d(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance()
+                .log(LogLevel.DEBUG, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void v(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString,
+                args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void i(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void e(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance()
+                .log(LogLevel.ERROR, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */
+    public static boolean isEnabled(IProtoLogGroup group) {
+        return group.isLogToLogcat()
+                || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
+    }
+
+    /**
+     * Returns the single instance of the ProtoLogImpl singleton class.
+     */
+    public static synchronized ShellProtoLogImpl getSingleInstance() {
+        if (sServiceInstance == null) {
+            sServiceInstance = new ShellProtoLogImpl();
+        }
+        return sServiceInstance;
+    }
+
+    public void startTextLogging(Context context, String... groups) {
+        try {
+            mViewerConfig.loadViewerConfig(
+                    context.getResources().openRawResource(R.raw.wm_shell_protolog));
+            setLogging(true /* setTextLogging */, true, mSystemOutWriter, groups);
+        } catch (IOException e) {
+            Log.i(TAG, "Unable to load log definitions: IOException while reading "
+                    + "wm_shell_protolog. " + e);
+        } catch (JSONException e) {
+            Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading "
+                    + "wm_shell_protolog. " + e);
+        }
+    }
+
+    public void stopTextLogging(String... groups) {
+        setLogging(true /* setTextLogging */, false, mSystemOutWriter, groups);
+    }
+
+    private ShellProtoLogImpl() {
+        super(new File(LOG_FILENAME), null, BUFFER_CAPACITY,
+                new ProtoLogViewerConfigReader());
+        mSystemOutWriter = new PrintWriter(System.out, true);
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerHandleView.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerHandleView.java
index a10242a..2cb1fff 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerHandleView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -28,43 +28,41 @@
 import android.util.Property;
 import android.view.View;
 
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.Interpolators;
 
 /**
  * View for the handle in the docked stack divider.
  */
-class DividerHandleView extends View {
+public class DividerHandleView extends View {
 
-    private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY
-            = new Property<DividerHandleView, Integer>(Integer.class, "width") {
+    private static final Property<DividerHandleView, Integer> WIDTH_PROPERTY =
+            new Property<DividerHandleView, Integer>(Integer.class, "width") {
+                @Override
+                public Integer get(DividerHandleView object) {
+                    return object.mCurrentWidth;
+                }
 
-        @Override
-        public Integer get(DividerHandleView object) {
-            return object.mCurrentWidth;
-        }
+                @Override
+                public void set(DividerHandleView object, Integer value) {
+                    object.mCurrentWidth = value;
+                    object.invalidate();
+                }
+            };
 
-        @Override
-        public void set(DividerHandleView object, Integer value) {
-            object.mCurrentWidth = value;
-            object.invalidate();
-        }
-    };
+    private static final Property<DividerHandleView, Integer> HEIGHT_PROPERTY =
+            new Property<DividerHandleView, Integer>(Integer.class, "height") {
+                @Override
+                public Integer get(DividerHandleView object) {
+                    return object.mCurrentHeight;
+                }
 
-    private final static Property<DividerHandleView, Integer> HEIGHT_PROPERTY
-            = new Property<DividerHandleView, Integer>(Integer.class, "height") {
-
-        @Override
-        public Integer get(DividerHandleView object) {
-            return object.mCurrentHeight;
-        }
-
-        @Override
-        public void set(DividerHandleView object, Integer value) {
-            object.mCurrentHeight = value;
-            object.invalidate();
-        }
-    };
+                @Override
+                public void set(DividerHandleView object, Integer value) {
+                    object.mCurrentHeight = value;
+                    object.invalidate();
+                }
+            };
 
     private final Paint mPaint = new Paint();
     private final int mWidth;
@@ -86,7 +84,7 @@
         mCircleDiameter = (mWidth + mHeight) / 3;
     }
 
-    public void setTouching(boolean touching, boolean animate) {
+    void setTouching(boolean touching, boolean animate) {
         if (touching == mTouching) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java
index 64ee7ed5..ff617ed 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerImeController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
@@ -22,6 +22,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.util.Slog;
@@ -29,9 +30,6 @@
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
-
-import androidx.annotation.Nullable;
 
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.TransactionPool;
@@ -45,6 +43,7 @@
     private final SplitScreenTaskOrganizer mSplits;
     private final TransactionPool mTransactionPool;
     private final Handler mHandler;
+    private final TaskOrganizer mTaskOrganizer;
 
     /**
      * These are the y positions of the top of the IME surface when it is hidden and when it is
@@ -93,10 +92,12 @@
     private boolean mPausedTargetAdjusted = false;
     private boolean mAdjustedWhileHidden = false;
 
-    DividerImeController(SplitScreenTaskOrganizer splits, TransactionPool pool, Handler handler) {
+    DividerImeController(SplitScreenTaskOrganizer splits, TransactionPool pool, Handler handler,
+            TaskOrganizer taskOrganizer) {
         mSplits = splits;
         mTransactionPool = pool;
         mHandler = handler;
+        mTaskOrganizer = taskOrganizer;
     }
 
     private DividerView getView() {
@@ -112,7 +113,7 @@
     }
 
     private boolean getSecondaryHasFocus(int displayId) {
-        WindowContainerToken imeSplit = TaskOrganizer.getImeTarget(displayId);
+        WindowContainerToken imeSplit = mTaskOrganizer.getImeTarget(displayId);
         return imeSplit != null
                 && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder());
     }
@@ -237,7 +238,7 @@
             }
 
             if (!mSplits.mSplitScreenController.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
-                WindowOrganizer.applyTransaction(wct);
+                mTaskOrganizer.applyTransaction(wct);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerState.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerState.java
index 8e79d51..23d86a0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerState.java
@@ -11,15 +11,15 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 /**
  * Class to hold state of divider that needs to persist across configuration changes.
  */
-public class DividerState {
+final class DividerState {
     public boolean animateAfterRecentsDrawn;
     public float mRatioPositionBeforeMinimized;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
index 95f048b..00146e9 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
@@ -62,11 +62,9 @@
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.FlingAnimationUtils;
+import com.android.wm.shell.animation.Interpolators;
 
 import java.util.function.Consumer;
 
@@ -78,7 +76,7 @@
     private static final String TAG = "DividerView";
     private static final boolean DEBUG = SplitScreenController.DEBUG;
 
-    public interface DividerCallbacks {
+    interface DividerCallbacks {
         void onDraggingStart();
         void onDraggingEnd();
     }
@@ -138,6 +136,7 @@
     private final Rect mOtherInsetRect = new Rect();
     private final Rect mLastResizeRect = new Rect();
     private final Rect mTmpRect = new Rect();
+    private SplitScreenController mSplitScreenController;
     private WindowManagerProxy mWindowManagerProxy;
     private DividerWindowManager mWindowManager;
     private VelocityTracker mVelocityTracker;
@@ -188,7 +187,7 @@
                 if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) {
                     // Only show the middle target if there are more than 1 split target
                     info.addAction(new AccessibilityAction(R.id.action_move_tl_50,
-                        mContext.getString(R.string.accessibility_action_divider_top_50)));
+                            mContext.getString(R.string.accessibility_action_divider_top_50)));
                 }
                 if (snapAlgorithm.isLastSplitTargetAvailable()) {
                     info.addAction(new AccessibilityAction(R.id.action_move_tl_30,
@@ -206,7 +205,7 @@
                 if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) {
                     // Only show the middle target if there are more than 1 split target
                     info.addAction(new AccessibilityAction(R.id.action_move_tl_50,
-                        mContext.getString(R.string.accessibility_action_divider_left_50)));
+                            mContext.getString(R.string.accessibility_action_divider_left_50)));
                 }
                 if (snapAlgorithm.isLastSplitTargetAvailable()) {
                     info.addAction(new AccessibilityAction(R.id.action_move_tl_30,
@@ -353,9 +352,11 @@
         }
     }
 
-    public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
+    void injectDependencies(SplitScreenController splitScreenController,
+            DividerWindowManager windowManager, DividerState dividerState,
             DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl,
             DividerImeController imeController, WindowManagerProxy wmProxy) {
+        mSplitScreenController = splitScreenController;
         mWindowManager = windowManager;
         mState = dividerState;
         mCallback = callback;
@@ -372,6 +373,7 @@
         }
     }
 
+    /** Gets non-minimized secondary bounds of split screen. */
     public Rect getNonMinimizedSplitScreenSecondaryBounds() {
         mOtherTaskRect.set(mSplitLayout.mSecondary);
         return mOtherTaskRect;
@@ -408,6 +410,7 @@
         return mSurfaceHidden;
     }
 
+    /** Starts dragging the divider bar. */
     public boolean startDragging(boolean animate, boolean touching) {
         cancelFlingAnimation();
         if (touching) {
@@ -426,6 +429,7 @@
         return inSplitMode();
     }
 
+    /** Stops dragging the divider bar. */
     public void stopDragging(int position, float velocity, boolean avoidDismissStart,
             boolean logMetrics) {
         mHandle.setTouching(false, true /* animate */);
@@ -615,7 +619,7 @@
             mEntranceAnimationRunning = false;
             mExitAnimationRunning = false;
             if (!dismissed && !wasMinimizeInteraction) {
-                WindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
+                mWindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
             }
             if (mCallback != null) {
                 mCallback.onDraggingEnd();
@@ -697,8 +701,7 @@
                 mTmpRect.top = 0;
                 break;
         }
-        Dependency.get(OverviewProxyService.class)
-                .notifySplitScreenBoundsChanged(mOtherTaskRect, mTmpRect);
+        mSplitScreenController.notifyBoundsChanged(mOtherTaskRect, mTmpRect);
     }
 
     private void cancelFlingAnimation() {
@@ -886,10 +889,10 @@
         t.hide(sc).apply();
         mTiles.releaseTransaction(t);
         int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
-        WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
+        mWindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
     }
 
-    public void setMinimizedDockStack(boolean minimized, long animDuration,
+    void setMinimizedDockStack(boolean minimized, long animDuration,
             boolean isHomeStackResizable) {
         if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized);
         mHomeStackResizable = isHomeStackResizable;
@@ -925,7 +928,7 @@
         }
     }
 
-    public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
+    void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
         if (mAdjustedForIme == adjustedForIme) {
             return;
         }
@@ -952,8 +955,8 @@
 
     private void saveSnapTargetBeforeMinimized(SnapTarget target) {
         mSnapTargetBeforeMinimized = target;
-        mState.mRatioPositionBeforeMinimized = (float) target.position /
-                (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+        mState.mRatioPositionBeforeMinimized = (float) target.position
+                / (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
                         : mSplitLayout.mDisplayLayout.width());
     }
 
@@ -971,8 +974,8 @@
     }
 
     private void repositionSnapTargetBeforeMinimized() {
-        int position = (int) (mState.mRatioPositionBeforeMinimized *
-                (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+        int position = (int) (mState.mRatioPositionBeforeMinimized
+                * (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
                         : mSplitLayout.mDisplayLayout.width()));
 
         // Set the snap target before minimized but do not save until divider is attached and not
@@ -1011,7 +1014,7 @@
                 containingRect.right, containingRect.bottom);
     }
 
-    public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
+    private void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
         DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect,
                 mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(),
                 mDividerSize);
@@ -1240,8 +1243,8 @@
         if (dismissTarget != null && fraction > 0f
                 && isDismissing(splitTarget, position, dockSide)) {
             fraction = calculateParallaxDismissingFraction(fraction, dockSide);
-            int offsetPosition = (int) (start +
-                    fraction * (dismissTarget.position - splitTarget.position));
+            int offsetPosition = (int) (start + fraction
+                    * (dismissTarget.position - splitTarget.position));
             int width = taskRect.width();
             int height = taskRect.height();
             switch (dockSide) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java
index d869333..0b4e17c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerWindowManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -37,7 +37,7 @@
 /**
  * Manages the window parameters of the docked stack divider.
  */
-public class DividerWindowManager {
+final class DividerWindowManager {
 
     private static final String WINDOW_TITLE = "DockedStackDivider";
 
@@ -45,12 +45,12 @@
     private WindowManager.LayoutParams mLp;
     private View mView;
 
-    public DividerWindowManager(SystemWindows systemWindows) {
+    DividerWindowManager(SystemWindows systemWindows) {
         mSystemWindows = systemWindows;
     }
 
     /** Add a divider view */
-    public void add(View view, int width, int height, int displayId) {
+    void add(View view, int width, int height, int displayId) {
         mLp = new WindowManager.LayoutParams(
                 width, height, TYPE_DOCK_DIVIDER,
                 FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
@@ -67,14 +67,14 @@
         mView = view;
     }
 
-    public void remove() {
+    void remove() {
         if (mView != null) {
             mSystemWindows.removeView(mView);
         }
         mView = null;
     }
 
-    public void setSlippery(boolean slippery) {
+    void setSlippery(boolean slippery) {
         boolean changed = false;
         if (slippery && (mLp.flags & FLAG_SLIPPERY) == 0) {
             mLp.flags |= FLAG_SLIPPERY;
@@ -88,7 +88,7 @@
         }
     }
 
-    public void setTouchable(boolean touchable) {
+    void setTouchable(boolean touchable) {
         if (mView == null) {
             return;
         }
@@ -106,7 +106,7 @@
     }
 
     /** Sets the touch region to `touchRegion`. Use null to unset.*/
-    public void setTouchRegion(Region touchRegion) {
+    void setTouchRegion(Region touchRegion) {
         if (mView == null) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivity.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivity.java
index 02f7505..7a16335 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivity.java
@@ -11,10 +11,10 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
@@ -29,7 +29,7 @@
 import android.view.View.OnTouchListener;
 import android.widget.TextView;
 
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 
 /**
  * Translucent activity that gets started on top of a task in multi-window to inform the user that
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivityController.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivityController.java
index 4c26694..1ef142d 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ForcedResizableInfoActivityController.java
@@ -11,13 +11,13 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
-import static com.android.systemui.stackdivider.ForcedResizableInfoActivity
-        .EXTRA_FORCED_RESIZEABLE_REASON;
+
+import static com.android.wm.shell.splitscreen.ForcedResizableInfoActivity.EXTRA_FORCED_RESIZEABLE_REASON;
 
 import android.app.ActivityOptions;
 import android.content.Context;
@@ -27,7 +27,7 @@
 import android.util.ArraySet;
 import android.widget.Toast;
 
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 
 import java.util.function.Consumer;
 
@@ -55,20 +55,20 @@
 
     /** Record of force resized task that's pending to be handled. */
     private class PendingTaskRecord {
-        int taskId;
+        int mTaskId;
         /**
          * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or
          * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY}
          */
-        int reason;
+        int mReason;
 
         PendingTaskRecord(int taskId, int reason) {
-            this.taskId = taskId;
-            this.reason = reason;
+            this.mTaskId = taskId;
+            this.mReason = reason;
         }
     }
 
-    public ForcedResizableInfoActivityController(Context context,
+    ForcedResizableInfoActivityController(Context context,
             SplitScreenController splitScreenController) {
         mContext = context;
         splitScreenController.registerInSplitScreenListener(mDockedStackExistsListener);
@@ -116,11 +116,11 @@
             PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i);
             Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
             ActivityOptions options = ActivityOptions.makeBasic();
-            options.setLaunchTaskId(pendingRecord.taskId);
+            options.setLaunchTaskId(pendingRecord.mTaskId);
             // Set as task overlay and allow to resume, so that when an app enters split-screen and
             // becomes paused, the overlay will still be shown.
             options.setTaskOverlay(true, true /* canResume */);
-            intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.reason);
+            intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.mReason);
             mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
         }
         mPendingTasks.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MinimizedDockShadow.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MinimizedDockShadow.java
index ecff54f..06f4ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/MinimizedDockShadow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MinimizedDockShadow.java
@@ -11,10 +11,10 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -27,7 +27,7 @@
 import android.view.View;
 import android.view.WindowManager;
 
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 
 /**
  * Shadow for the minimized dock state on homescreen.
@@ -42,7 +42,7 @@
         super(context, attrs);
     }
 
-    public void setDockSide(int dockSide) {
+    void setDockSide(int dockSide) {
         if (dockSide != mDockSide) {
             mDockSide = dockSide;
             updatePaint(getLeft(), getTop(), getRight(), getBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java
index a34e85517..3c0f939 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDisplayLayout.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 91850cc..184342f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
+import android.graphics.Rect;
 import android.window.WindowContainerToken;
 
 import java.io.PrintWriter;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -81,6 +83,9 @@
     /** Registers listener that gets called whenever the existence of the divider changes. */
     void registerInSplitScreenListener(Consumer<Boolean> listener);
 
+    /** Registers listener that gets called whenever the split screen bounds changes. */
+    void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener);
+
     /** @return the container token for the secondary split root task. */
     WindowContainerToken getSecondaryRoot();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 11773f6..eed5092 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -29,12 +29,12 @@
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.R;
+import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
@@ -46,6 +46,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -70,9 +71,12 @@
     private final SystemWindows mSystemWindows;
     final TransactionPool mTransactionPool;
     private final WindowManagerProxy mWindowManagerProxy;
+    private final TaskOrganizer mTaskOrganizer;
 
     private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
             new ArrayList<>();
+    private final ArrayList<WeakReference<BiConsumer<Rect, Rect>>> mBoundsChangedListeners =
+            new ArrayList<>();
 
 
     private DividerWindowManager mWindowManager;
@@ -103,9 +107,12 @@
         mHandler = handler;
         mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
         mTransactionPool = transactionPool;
-        mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler);
+        mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler,
+                shellTaskOrganizer);
+        mTaskOrganizer = shellTaskOrganizer;
         mSplits = new SplitScreenTaskOrganizer(this, shellTaskOrganizer);
-        mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
+        mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler,
+                shellTaskOrganizer);
         mRotationController =
                 (display, fromRotation, toRotation, wct) -> {
                     if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) {
@@ -129,7 +136,7 @@
                     sdl.resizeSplits(target.position, t);
 
                     if (isSplitActive() && mHomeStackResizable) {
-                        WindowManagerProxy
+                        mWindowManagerProxy
                                 .applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
                     }
                     if (mWindowManagerProxy.queueSyncTransactionIfWaiting(t)) {
@@ -186,7 +193,7 @@
             final WindowContainerTransaction tct = new WindowContainerTransaction();
             int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
             mSplitLayout.resizeSplits(midPos, tct);
-            WindowOrganizer.applyTransaction(tct);
+            mTaskOrganizer.applyTransaction(tct);
         } catch (Exception e) {
             Slog.e(TAG, "Failed to register docked stack listener", e);
             removeDivider();
@@ -205,7 +212,7 @@
             int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
             final WindowContainerTransaction tct = new WindowContainerTransaction();
             mSplitLayout.resizeSplits(midPos, tct);
-            WindowOrganizer.applyTransaction(tct);
+            mTaskOrganizer.applyTransaction(tct);
         } else if (mSplitLayout.mDisplayLayout.rotation()
                 == mRotateSplitLayout.mDisplayLayout.rotation()) {
             mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
@@ -257,8 +264,8 @@
         mView = (DividerView)
                 LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
         DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
-        mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits,
-                mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
+        mView.injectDependencies(this, mWindowManager, mDividerState, mForcedResizableController,
+                mSplits, mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
         mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
         mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
         final int size = dctx.getResources().getDimensionPixelSize(
@@ -369,7 +376,7 @@
         // If we are only setting focusability, a sync transaction isn't necessary (in fact it
         // can interrupt other animations), so see if it can be submitted on pending instead.
         if (!mWindowManagerProxy.queueSyncTransactionIfWaiting(wct)) {
-            WindowOrganizer.applyTransaction(wct);
+            mTaskOrganizer.applyTransaction(wct);
         }
     }
 
@@ -466,6 +473,24 @@
         }
     }
 
+    @Override
+    public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
+        synchronized (mBoundsChangedListeners) {
+            mBoundsChangedListeners.add(new WeakReference<>(listener));
+        }
+    }
+
+    /** Notifies the bounds of split screen changed. */
+    void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
+        synchronized (mBoundsChangedListeners) {
+            mBoundsChangedListeners.removeIf(wf -> {
+                BiConsumer<Rect, Rect> l = wf.get();
+                if (l != null) l.accept(secondaryWindowBounds, secondaryWindowInsets);
+                return l == null;
+            });
+        }
+    }
+
     void startEnterSplit() {
         update(mDisplayController.getDisplayContext(
                 mContext.getDisplayId()).getResources().getConfiguration());
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
index 325c559..30bc43b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -31,7 +31,6 @@
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
-import android.window.TaskOrganizer;
 
 import com.android.wm.shell.ShellTaskOrganizer;
 
@@ -64,9 +63,9 @@
     void init() throws RemoteException {
         synchronized (this) {
             try {
-                mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
+                mPrimary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
                         WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-                mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
+                mSecondary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
                         WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
             } catch (Exception e) {
                 // teardown to prevent callbacks
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
index 82b10bd..015707e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/WindowManagerProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.stackdivider;
+package com.android.wm.shell.splitscreen;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -40,6 +40,7 @@
 import android.window.WindowOrganizer;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
 
 import java.util.ArrayList;
@@ -82,8 +83,12 @@
         }
     };
 
-    WindowManagerProxy(TransactionPool transactionPool, Handler handler) {
+    private final TaskOrganizer mTaskOrganizer;
+
+    WindowManagerProxy(TransactionPool transactionPool, Handler handler,
+            TaskOrganizer taskOrganizer) {
         mSyncTransactionQueue = new SyncTransactionQueue(transactionPool, handler);
+        mTaskOrganizer = taskOrganizer;
     }
 
     void dismissOrMaximizeDocked(final SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
@@ -112,18 +117,18 @@
         mExecutor.execute(mSetTouchableRegionRunnable);
     }
 
-    static void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
+    void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
         WindowContainerTransaction t = new WindowContainerTransaction();
         splitLayout.resizeSplits(position, t);
-        WindowOrganizer.applyTransaction(t);
+        new WindowOrganizer().applyTransaction(t);
     }
 
-    private static boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out,
+    private boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out,
             WindowContainerToken parent) {
         boolean resizable = false;
         List<ActivityManager.RunningTaskInfo> rootTasks = parent == null
-                ? TaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
-                : TaskOrganizer.getChildTasks(parent, HOME_AND_RECENTS);
+                ? mTaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
+                : mTaskOrganizer.getChildTasks(parent, HOME_AND_RECENTS);
         for (int i = 0, n = rootTasks.size(); i < n; ++i) {
             final ActivityManager.RunningTaskInfo ti = rootTasks.get(i);
             out.add(ti);
@@ -139,7 +144,7 @@
      * split is minimized. This actually "sticks out" of the secondary split area, but when in
      * minimized mode, the secondary split gets a 'negative' crop to expose it.
      */
-    static boolean applyHomeTasksMinimized(SplitDisplayLayout layout, WindowContainerToken parent,
+    boolean applyHomeTasksMinimized(SplitDisplayLayout layout, WindowContainerToken parent,
             @NonNull WindowContainerTransaction wct) {
         // Resize the home/recents stacks to the larger minimized-state size
         final Rect homeBounds;
@@ -191,9 +196,9 @@
         // Set launchtile first so that any stack created after
         // getAllStackInfos and before reparent (even if unlikely) are placed
         // correctly.
-        TaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token);
+        mTaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token);
         List<ActivityManager.RunningTaskInfo> rootTasks =
-                TaskOrganizer.getRootTasks(DEFAULT_DISPLAY, null /* activityTypes */);
+                mTaskOrganizer.getRootTasks(DEFAULT_DISPLAY, null /* activityTypes */);
         WindowContainerTransaction wct = new WindowContainerTransaction();
         if (rootTasks.isEmpty()) {
             return false;
@@ -229,7 +234,7 @@
         return isHomeResizable;
     }
 
-    static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
+    boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
         final int atype = ti.configuration.windowConfiguration.getActivityType();
         return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
     }
@@ -244,18 +249,18 @@
             boolean dismissOrMaximize) {
         // Set launch root first so that any task created after getChildContainers and
         // before reparent (pretty unlikely) are put into fullscreen.
-        TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
+        mTaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
         // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
         //                 plus specific APIs to clean this up.
         List<ActivityManager.RunningTaskInfo> primaryChildren =
-                TaskOrganizer.getChildTasks(tiles.mPrimary.token, null /* activityTypes */);
+                mTaskOrganizer.getChildTasks(tiles.mPrimary.token, null /* activityTypes */);
         List<ActivityManager.RunningTaskInfo> secondaryChildren =
-                TaskOrganizer.getChildTasks(tiles.mSecondary.token, null /* activityTypes */);
+                mTaskOrganizer.getChildTasks(tiles.mSecondary.token, null /* activityTypes */);
         // In some cases (eg. non-resizable is launched), system-server will leave split-screen.
         // as a result, the above will not capture any tasks; yet, we need to clean-up the
         // home task bounds.
         List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
-                TaskOrganizer.getRootTasks(DEFAULT_DISPLAY, HOME_AND_RECENTS);
+                mTaskOrganizer.getRootTasks(DEFAULT_DISPLAY, HOME_AND_RECENTS);
         // Filter out the root split tasks
         freeHomeAndRecents.removeIf(p -> p.token.equals(tiles.mSecondary.token)
                 || p.token.equals(tiles.mPrimary.token));
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 692e2fa..937b00b 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -25,6 +25,7 @@
         "androidx.test.ext.junit",
         "mockito-target-extended-minus-junit4",
         "truth-prebuilt",
+        "testables",
     ],
     libs: [
         "android.test.mock",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 10672c8..497b6b7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -20,10 +20,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.content.res.Configuration;
+import android.os.RemoteException;
 import android.view.SurfaceControl;
+import android.window.ITaskOrganizer;
+import android.window.ITaskOrganizerController;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -31,6 +35,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 
@@ -41,6 +47,9 @@
 @RunWith(AndroidJUnit4.class)
 public class ShellTaskOrganizerTests {
 
+    @Mock
+    private ITaskOrganizerController mTaskOrganizerController;
+
     ShellTaskOrganizer mOrganizer;
 
     private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
@@ -71,7 +80,15 @@
 
     @Before
     public void setUp() {
-        mOrganizer = new ShellTaskOrganizer();
+        MockitoAnnotations.initMocks(this);
+        mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController);
+    }
+
+    @Test
+    public void registerOrganizer_sendRegisterTaskOrganizer() throws RemoteException {
+        mOrganizer.registerOrganizer();
+
+        verify(mTaskOrganizerController).registerTaskOrganizer(any(ITaskOrganizer.class));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
index 7fabf82..a8a3a9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static org.junit.Assert.assertNotNull;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
similarity index 61%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 02d587d..1ce8b54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -22,21 +22,20 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.wm.shell.common.DisplayController;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -52,8 +51,6 @@
     OneHandedTimeoutHandler mTimeoutHandler;
 
     @Mock
-    CommandQueue mCommandQueue;
-    @Mock
     DisplayController mMockDisplayController;
     @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@@ -64,21 +61,20 @@
     @Mock
     OneHandedGestureHandler mMockGestureHandler;
     @Mock
-    SysUiState mMockSysUiState;
+    OneHandedTimeoutHandler mMockTimeoutHandler;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mDisplay = mContext.getDisplay();
-        mOneHandedController = new OneHandedController(
-                getContext(),
-                mCommandQueue,
+        OneHandedController oneHandedController = new OneHandedController(
+                mContext,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
                 mMockTouchHandler,
                 mMockTutorialHandler,
-                mMockGestureHandler,
-                mMockSysUiState);
+                mMockGestureHandler);
+        mOneHandedController = Mockito.spy(oneHandedController);
         mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
 
         when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
@@ -97,7 +93,7 @@
 
     @Test
     public void testRegisterOrganizer() {
-        verify(mMockDisplayAreaOrganizer).registerOrganizer(anyInt());
+        verify(mMockDisplayAreaOrganizer, atLeastOnce()).registerOrganizer(anyInt());
     }
 
     @Test
@@ -116,11 +112,22 @@
     }
 
     @Test
-    public void testRegisterTransitionCallback() {
-        verify(mMockDisplayAreaOrganizer, atLeastOnce()).registerTransitionCallback(any());
+    public void testRegisterTransitionCallbackAfterInit() {
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTouchHandler);
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockGestureHandler);
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler);
     }
 
     @Test
+    public void testRegisterTransitionCallback() {
+        OneHandedTransitionCallback callback = new OneHandedTransitionCallback() {};
+        mOneHandedController.registerTransitionCallback(callback);
+
+        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(callback);
+    }
+
+
+    @Test
     public void testStopOneHanded_shouldRemoveTimer() {
         mOneHandedController.stopOneHanded();
 
@@ -132,7 +139,7 @@
         final boolean enabled = true;
         mOneHandedController.setOneHandedEnabled(enabled);
 
-        verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled);
+        verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled);
     }
 
     @Test
@@ -140,6 +147,44 @@
         final boolean enabled = true;
         mOneHandedController.setSwipeToNotificationEnabled(enabled);
 
-        verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled);
+        verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled);
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void tesSettingsObserver_updateTapAppToExit() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.TAPS_APP_TO_EXIT, 1);
+
+        verify(mOneHandedController).setTaskChangeToExit(true);
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void tesSettingsObserver_updateEnabled() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
+
+        verify(mOneHandedController).setOneHandedEnabled(true);
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void tesSettingsObserver_updateTimeout() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
+                OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
+
+        verify(mMockTimeoutHandler).setTimeout(
+                OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void tesSettingsObserver_updateSwipeToNotification() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
+
+        verify(mOneHandedController).setSwipeToNotificationEnabled(true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index a989cd1f..5ff94b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedEventsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedEventsTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java
index 36c1174..492c34e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedEventsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedEventsTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static org.junit.Assert.assertEquals;
 
@@ -22,7 +22,6 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -34,7 +33,7 @@
 
 @RunWith(Parameterized.class)
 @SmallTest
-public class OneHandedEventsTest extends SysuiTestCase {
+public class OneHandedEventsTest extends OneHandedTestCase {
 
     private UiEventLoggerFake mUiEventLogger;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
new file mode 100644
index 0000000..fb417c8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.onehanded;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.DisplayController;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OneHandedGestureHandlerTest extends OneHandedTestCase {
+    OneHandedTutorialHandler mTutorialHandler;
+    OneHandedGestureHandler mGestureHandler;
+    @Mock
+    DisplayController mMockDisplayController;
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mTutorialHandler = new OneHandedTutorialHandler(mContext);
+        mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
+    }
+
+    @Test
+    public void testSetGestureEventListener() {
+        OneHandedGestureHandler.OneHandedGestureEventCallback callback = 
+            new OneHandedGestureHandler.OneHandedGestureEventCallback() {
+                @Override
+                public void onStart() {}
+
+                @Override
+                public void onStop() {}
+            };
+
+        mGestureHandler.setGestureEventListener(callback);
+        assertThat(mGestureHandler.mGestureEventCallback).isEqualTo(callback);
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void testReceiveNewConfig_whenThreeButtonModeEnabled() {
+        mGestureHandler.onOneHandedEnabled(true);
+        mGestureHandler.onThreeButtonModeEnabled(true);
+
+        assertThat(mGestureHandler.mInputMonitor).isNotNull();
+        assertThat(mGestureHandler.mInputEventReceiver).isNotNull();
+    }
+
+    @Test
+    public void testOneHandedDisabled_shouldDisposeInputChannel() {
+        mGestureHandler.onOneHandedEnabled(false);
+
+        assertThat(mGestureHandler.mInputMonitor).isNull();
+        assertThat(mGestureHandler.mInputEventReceiver).isNull();
+    }
+
+    @Test
+    public void testChangeNavBarToNon3Button_shouldDisposeInputChannel() {
+        mGestureHandler.onOneHandedEnabled(true);
+        mGestureHandler.onThreeButtonModeEnabled(false);
+
+        assertThat(mGestureHandler.mInputMonitor).isNull();
+        assertThat(mGestureHandler.mInputEventReceiver).isNull();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
similarity index 83%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
index 990eb63..7c11138 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -59,24 +59,24 @@
     @Test
     public void testRegisterSecureKeyObserver() {
         final Uri result = OneHandedSettingsUtil.registerSettingsKeyObserver(
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver);
+                Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
 
         assertThat(result).isNotNull();
 
         OneHandedSettingsUtil.registerSettingsKeyObserver(
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver);
+                Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
     }
 
     @Test
     public void testUnregisterSecureKeyObserver() {
         OneHandedSettingsUtil.registerSettingsKeyObserver(
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, mContentResolver, mContentObserver);
+                Settings.Secure.TAPS_APP_TO_EXIT, mContentResolver, mContentObserver);
         OneHandedSettingsUtil.unregisterSettingsKeyObserver(mContentResolver, mContentObserver);
 
         assertThat(mOnChanged).isFalse();
 
         Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, 0);
+                Settings.Secure.TAPS_APP_TO_EXIT, 0);
 
         assertThat(mOnChanged).isFalse();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
similarity index 67%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
index 04ebf25..c7ae2a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
@@ -14,13 +14,21 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.SystemProperties;
 import android.provider.Settings;
 
-import com.android.systemui.SysuiTestCase;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.Before;
@@ -28,14 +36,26 @@
 /**
  * Base class that does One Handed specific setup.
  */
-public abstract class OneHandedTestCase extends SysuiTestCase {
+public abstract class OneHandedTestCase {
     static boolean sOrigEnabled;
     static boolean sOrigTapsAppToExitEnabled;
     static int sOrigTimeout;
     static boolean sOrigSwipeToNotification;
 
+    protected Context mContext;
+
     @Before
     public void setupSettings() {
+        final Context testContext =
+                InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final DisplayManager dm = testContext.getSystemService(DisplayManager.class);
+        mContext = testContext.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY));
+
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity();
+
         sOrigEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
                 getContext().getContentResolver());
         sOrigTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
@@ -54,6 +74,11 @@
                 Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
     }
 
+    @Before
+    public void assumeOneHandedModeSupported() {
+        assumeTrue(SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false));
+    }
+
     @After
     public void restoreSettings() {
         Settings.Secure.putInt(getContext().getContentResolver(),
@@ -65,6 +90,15 @@
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
                 sOrigSwipeToNotification ? 1 : 0);
+
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    protected Context getContext() {
+        return mContext;
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTimeoutHandlerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
index 7d63131..e2b70c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTimeoutHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
-import static com.android.systemui.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
-import static com.android.systemui.onehanded.OneHandedTimeoutHandler.ONE_HANDED_TIMEOUT_STOP_MSG;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
+import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
+import static com.android.wm.shell.onehanded.OneHandedTimeoutHandler.ONE_HANDED_TIMEOUT_STOP_MSG;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
new file mode 100644
index 0000000..c69e385
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.onehanded;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OneHandedTouchHandlerTest extends OneHandedTestCase {
+    OneHandedTouchHandler mTouchHandler;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTouchHandler = new OneHandedTouchHandler();
+    }
+
+    @Test
+    public void testRegisterTouchEventListener() {
+        OneHandedTouchHandler.OneHandedTouchEventCallback callback = () -> {
+        };
+        mTouchHandler.registerTouchEventListener(callback);
+
+        assertThat(mTouchHandler.mTouchEventCallback).isEqualTo(callback);
+    }
+
+    @Test
+    public void testOneHandedDisabled_shouldDisposeInputChannel() {
+        mTouchHandler.onOneHandedEnabled(false);
+
+        assertThat(mTouchHandler.mInputMonitor).isNull();
+        assertThat(mTouchHandler.mInputEventReceiver).isNull();
+    }
+
+    @Ignore("b/167943723, refactor it and fix it")
+    @Test
+    public void testOneHandedEnabled_monitorInputChannel() {
+        mTouchHandler.onOneHandedEnabled(true);
+
+        assertThat(mTouchHandler.mInputMonitor).isNotNull();
+        assertThat(mTouchHandler.mInputEventReceiver).isNotNull();
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
similarity index 74%
rename from packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index 1bffbf7..4a133d39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.onehanded;
+package com.android.wm.shell.onehanded;
 
 import static org.mockito.Mockito.verify;
 
@@ -23,53 +23,40 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.wm.shell.common.DisplayController;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
+    @Mock
     OneHandedTouchHandler mTouchHandler;
     OneHandedTutorialHandler mTutorialHandler;
     OneHandedGestureHandler mGestureHandler;
     OneHandedController mOneHandedController;
     @Mock
-    CommandQueue mCommandQueue;
-    @Mock
     DisplayController mMockDisplayController;
     @Mock
-    NavigationModeController mMockNavigationModeController;
-    @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
-    @Mock
-    SysUiState mMockSysUiState;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTouchHandler = new OneHandedTouchHandler();
-        mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext));
-        mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
-                mMockNavigationModeController);
+        mTutorialHandler = new OneHandedTutorialHandler(mContext);
+        mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
         mOneHandedController = new OneHandedController(
                 getContext(),
-                mCommandQueue,
                 mMockDisplayController,
                 mMockDisplayAreaOrganizer,
                 mTouchHandler,
                 mTutorialHandler,
-                mGestureHandler,
-                mMockSysUiState);
+                mGestureHandler);
     }
 
     @Test
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 76ec078..4dbce92 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -84,7 +84,6 @@
 
     mGraphicsQueue = VK_NULL_HANDLE;
     mAHBUploadQueue = VK_NULL_HANDLE;
-    mPresentQueue = VK_NULL_HANDLE;
     mDevice = VK_NULL_HANDLE;
     mPhysicalDevice = VK_NULL_HANDLE;
     mInstance = VK_NULL_HANDLE;
@@ -192,10 +191,6 @@
     }
     LOG_ALWAYS_FATAL_IF(mGraphicsQueueIndex == queueCount);
 
-    // All physical devices and queue families on Android must be capable of
-    // presentation with any native window. So just use the first one.
-    mPresentQueueIndex = 0;
-
     {
         uint32_t extensionCount = 0;
         err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount,
@@ -289,31 +284,21 @@
         queueNextPtr = &queuePriorityCreateInfo;
     }
 
-    const VkDeviceQueueCreateInfo queueInfo[2] = {
-            {
-                    VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,  // sType
-                    queueNextPtr,                                // pNext
-                    0,                                           // VkDeviceQueueCreateFlags
-                    mGraphicsQueueIndex,                         // queueFamilyIndex
-                    2,                                           // queueCount
-                    queuePriorities,                             // pQueuePriorities
-            },
-            {
-                    VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,  // sType
-                    queueNextPtr,                                // pNext
-                    0,                                           // VkDeviceQueueCreateFlags
-                    mPresentQueueIndex,                          // queueFamilyIndex
-                    1,                                           // queueCount
-                    queuePriorities,                             // pQueuePriorities
-            }};
-    uint32_t queueInfoCount = (mPresentQueueIndex != mGraphicsQueueIndex) ? 2 : 1;
+    const VkDeviceQueueCreateInfo queueInfo = {
+            VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,  // sType
+            queueNextPtr,                                // pNext
+            0,                                           // VkDeviceQueueCreateFlags
+            mGraphicsQueueIndex,                         // queueFamilyIndex
+            2,                                           // queueCount
+            queuePriorities,                             // pQueuePriorities
+    };
 
     const VkDeviceCreateInfo deviceInfo = {
             VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,  // sType
             &features,                             // pNext
             0,                                     // VkDeviceCreateFlags
-            queueInfoCount,                        // queueCreateInfoCount
-            queueInfo,                             // pQueueCreateInfos
+            1,                                     // queueCreateInfoCount
+            &queueInfo,                            // pQueueCreateInfos
             0,                                     // layerCount
             nullptr,                               // ppEnabledLayerNames
             (uint32_t)mDeviceExtensions.size(),    // extensionCount
@@ -361,8 +346,6 @@
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue);
 
-    mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
-
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
     }
@@ -555,8 +538,8 @@
 
 void VulkanManager::destroySurface(VulkanSurface* surface) {
     // Make sure all submit commands have finished before starting to destroy objects.
-    if (VK_NULL_HANDLE != mPresentQueue) {
-        mQueueWaitIdle(mPresentQueue);
+    if (VK_NULL_HANDLE != mGraphicsQueue) {
+        mQueueWaitIdle(mGraphicsQueue);
     }
     mDeviceWaitIdle(mDevice);
 
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 75c05b8..7a77466 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -163,8 +163,6 @@
     uint32_t mGraphicsQueueIndex;
     VkQueue mGraphicsQueue = VK_NULL_HANDLE;
     VkQueue mAHBUploadQueue = VK_NULL_HANDLE;
-    uint32_t mPresentQueueIndex;
-    VkQueue mPresentQueue = VK_NULL_HANDLE;
 
     // Variables saved to populate VkFunctorInitParams.
     static const uint32_t mAPIVersion = VK_MAKE_VERSION(1, 1, 0);
diff --git a/libs/hwui/shader/BlurShader.cpp b/libs/hwui/shader/BlurShader.cpp
index 4d18cdd..fa10be1 100644
--- a/libs/hwui/shader/BlurShader.cpp
+++ b/libs/hwui/shader/BlurShader.cpp
@@ -26,7 +26,9 @@
             SkImageFilters::Blur(
                     Blur::convertRadiusToSigma(radiusX),
                     Blur::convertRadiusToSigma(radiusY),
-                    inputShader ? inputShader->asSkImageFilter() : nullptr)
+                    SkTileMode::kClamp,
+                    inputShader ? inputShader->asSkImageFilter() : nullptr,
+                    nullptr)
             ) { }
 
 sk_sp<SkImageFilter> BlurShader::makeSkImageFilter() {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1803027..04bcbfc 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -29,6 +29,8 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -162,7 +164,7 @@
     /**
      * The fused location provider.
      *
-     * <p>This provider combines may combine inputs from several location sources to provide the
+     * <p>This provider may combine inputs from several location sources to provide the
      * best possible location fix. It is implicitly used for all API's that involve the
      * {@link LocationRequest} object.
      *
@@ -215,6 +217,7 @@
      * @see #EXTRA_PROVIDER_ENABLED
      * @see #isProviderEnabled(String)
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
 
     /**
@@ -243,6 +246,7 @@
      * @see #EXTRA_LOCATION_ENABLED
      * @see #isLocationEnabled()
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
 
     /**
diff --git a/location/java/android/location/timezone/LocationTimeZoneEvent.java b/location/java/android/location/timezone/LocationTimeZoneEvent.java
index 540bdff..55bc507 100644
--- a/location/java/android/location/timezone/LocationTimeZoneEvent.java
+++ b/location/java/android/location/timezone/LocationTimeZoneEvent.java
@@ -35,7 +35,8 @@
  */
 public final class LocationTimeZoneEvent implements Parcelable {
 
-    @IntDef({ EVENT_TYPE_UNKNOWN, EVENT_TYPE_SUCCESS, EVENT_TYPE_SUCCESS })
+    @IntDef({ EVENT_TYPE_UNKNOWN, EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUCCESS,
+            EVENT_TYPE_UNCERTAIN })
     @interface EventType {}
 
     /** Uninitialized value for {@link #mEventType} - must not be used for real events. */
@@ -43,7 +44,7 @@
 
     /**
      * Indicates there was a permanent failure. This is not generally expected, and probably means a
-     * required backend service is no longer supported / available.
+     * required backend service has been turned down, or the client is unreasonably old.
      */
     public static final int EVENT_TYPE_PERMANENT_FAILURE = 1;
 
@@ -54,8 +55,9 @@
     public static final int EVENT_TYPE_SUCCESS = 2;
 
     /**
-     * Indicates the time zone is not known because there was a (temporary) error, e.g. when
-     * detecting location, or when resolving the location to a time zone.
+     * Indicates the time zone is not known because of an expected runtime state or error, e.g. when
+     * the provider is unable to detect location, or there was a problem when resolving the location
+     * to a time zone.
      */
     public static final int EVENT_TYPE_UNCERTAIN = 3;
 
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 0ab62c1..6c8b500 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -72,6 +72,11 @@
     private final @Role int mRole;
 
     /**
+     * The internal audio device type
+     */
+    private final int mNativeType;
+
+    /**
      * @hide
      * Constructor from a valid {@link AudioDeviceInfo}
      * @param deviceInfo the connected audio device from which to obtain the device-identifying
@@ -83,6 +88,7 @@
         mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
         mType = deviceInfo.getType();
         mAddress = deviceInfo.getAddress();
+        mNativeType = deviceInfo.getInternalType();
     }
 
     /**
@@ -101,9 +107,12 @@
         }
         if (role == ROLE_OUTPUT) {
             AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type);
-        }
-        if (role == ROLE_INPUT) {
+            mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(type);
+        } else if (role == ROLE_INPUT) {
             AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
+            mNativeType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(type);
+        } else {
+            mNativeType = AudioSystem.DEVICE_NONE;
         }
 
         mRole = role;
@@ -115,6 +124,7 @@
         mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
         mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
         mAddress = address;
+        mNativeType = nativeType;
     }
 
     /**
@@ -147,6 +157,15 @@
         return mAddress;
     }
 
+    /**
+     * @hide
+     * Returns the internal device type of a device
+     * @return the internal device type
+     */
+    public int getInternalType() {
+        return mNativeType;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(mRole, mType, mAddress);
@@ -189,12 +208,14 @@
         dest.writeInt(mRole);
         dest.writeInt(mType);
         dest.writeString(mAddress);
+        dest.writeInt(mNativeType);
     }
 
     private AudioDeviceAttributes(@NonNull Parcel in) {
         mRole = in.readInt();
         mType = in.readInt();
         mAddress = in.readString();
+        mNativeType = in.readInt();
     }
 
     public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR =
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d4fb1be..477519c 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -351,6 +351,14 @@
     }
 
     /**
+     * @hide
+     * @return the internal device tyoe
+     */
+    public int getInternalType() {
+        return mPort.type();
+    }
+
+    /**
      * @return The internal device ID.
      */
     public int getId() {
@@ -513,10 +521,21 @@
         return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, TYPE_UNKNOWN);
     }
 
+    /** @hide */
+    public static int convertDeviceTypeToInternalInputDevice(int deviceType) {
+        return EXT_TO_INT_INPUT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+    }
+
     private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
 
     private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
 
+    /**
+     * EXT_TO_INT_INPUT_DEVICE_MAPPING aims at mapping external device type to internal input device
+     * type.
+     */
+    private static final SparseIntArray EXT_TO_INT_INPUT_DEVICE_MAPPING;
+
     static {
         INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, TYPE_BUILTIN_EARPIECE);
@@ -601,6 +620,32 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_OUT_REMOTE_SUBMIX);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
+
+        // privileges mapping to input device
+        EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_WIRED_HEADSET, AudioSystem.DEVICE_IN_WIRED_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_HDMI, AudioSystem.DEVICE_IN_HDMI);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TELEPHONY, AudioSystem.DEVICE_IN_TELEPHONY_RX);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_DOCK, AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_USB_ACCESSORY, AudioSystem.DEVICE_IN_USB_ACCESSORY);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_DEVICE, AudioSystem.DEVICE_IN_USB_DEVICE);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_USB_HEADSET, AudioSystem.DEVICE_IN_USB_HEADSET);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_ANALOG, AudioSystem.DEVICE_IN_LINE);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_LINE_DIGITAL, AudioSystem.DEVICE_IN_SPDIF);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_IN_BLUETOOTH_A2DP);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_IN_IP);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_IN_BUS);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(
+                TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX);
+        EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET);
     }
 }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
old mode 100755
new mode 100644
index a16e063..e1e55c2
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1949,6 +1949,349 @@
     }
 
     //====================================================================
+    // Audio Capture Preset routing
+
+    /**
+     * @hide
+     * Set the preferred device for a given capture preset, i.e. the audio routing to be used by
+     * this capture preset. Note that the device may not be available at the time the preferred
+     * device is set, but it will be used once made available.
+     * <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference
+     * for this capture preset.</p>
+     * @param capturePreset the audio capture preset whose routing will be affected
+     * @param device the audio device to route to when available
+     * @return true if the operation was successful, false otherwise
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean setPreferredDeviceForCapturePreset(int capturePreset,
+                                                      @NonNull AudioDeviceAttributes device) {
+        return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
+    }
+
+    /**
+     * @hide
+     * Remove all the preferred audio devices previously set
+     * @param capturePreset the audio capture preset whose routing will be affected
+     * @return true if the operation was successful, false otherwise (invalid capture preset, or no
+     *     device set for example)
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
+        if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+            return false;
+        }
+        try {
+            final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset);
+            return status == AudioSystem.SUCCESS;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Return the preferred devices for an audio capture preset, previously set with
+     * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)}
+     * @param capturePreset the capture preset to query
+     * @return a list that contains preferred devices for that capture preset.
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+        if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+            return new ArrayList<AudioDeviceAttributes>();
+        }
+        try {
+            return getService().getPreferredDevicesForCapturePreset(capturePreset);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private boolean setPreferredDevicesForCapturePreset(
+            int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+        Objects.requireNonNull(devices);
+        if (!MediaRecorder.isValidAudioSource(capturePreset)) {
+            return false;
+        }
+        if (devices.size() != 1) {
+            throw new IllegalArgumentException(
+                    "Only support setting one preferred devices for capture preset");
+        }
+        for (AudioDeviceAttributes device : devices) {
+            Objects.requireNonNull(device);
+        }
+        try {
+            final int status =
+                    getService().setPreferredDevicesForCapturePreset(capturePreset, devices);
+            return status == AudioSystem.SUCCESS;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Interface to be notified of changes in the preferred audio devices set for a given capture
+     * preset.
+     * <p>Note that this listener will only be invoked whenever
+     * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or
+     * {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in
+     * preferred device. It will not be invoked directly after registration with
+     * {@link #addOnPreferredDevicesForCapturePresetChangedListener(
+     * Executor, OnPreferredDevicesForCapturePresetChangedListener)}
+     * to indicate which strategies had preferred devices at the time of registration.</p>
+     * @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+     * @see #clearPreferredDevicesForCapturePreset(int)
+     * @see #getPreferredDevicesForCapturePreset(int)
+     */
+    @SystemApi
+    public interface OnPreferredDevicesForCapturePresetChangedListener {
+        /**
+         * Called on the listener to indicate that the preferred audio devices for the given
+         * capture preset has changed.
+         * @param capturePreset the capture preset whose preferred device changed
+         * @param devices a list of newly set preferred audio devices
+         */
+        void onPreferredDevicesForCapturePresetChanged(
+                int capturePreset, @NonNull List<AudioDeviceAttributes> devices);
+    }
+
+    /**
+     * @hide
+     * Adds a listener for being notified of changes to the capture-preset-preferred audio device.
+     * @param executor
+     * @param listener
+     * @throws SecurityException if the caller doesn't hold the required permission
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void addOnPreferredDevicesForCapturePresetChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
+            throws SecurityException {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
+        int status = addOnDevRoleForCapturePresetChangedListener(
+                executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+        if (status == AudioSystem.ERROR) {
+            // This must not happen
+            throw new RuntimeException("Unknown error happened");
+        }
+        if (status == AudioSystem.BAD_VALUE) {
+            throw new IllegalArgumentException(
+                    "attempt to call addOnPreferredDevicesForCapturePresetChangedListener() "
+                            + "on a previously registered listener");
+        }
+    }
+
+    /**
+     * @hide
+     * Removes a previously added listener of changes to the capture-preset-preferred audio device.
+     * @param listener
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void removeOnPreferredDevicesForCapturePresetChangedListener(
+            @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
+        Objects.requireNonNull(listener);
+        int status = removeOnDevRoleForCapturePresetChangedListener(
+                listener, AudioSystem.DEVICE_ROLE_PREFERRED);
+        if (status == AudioSystem.ERROR) {
+            // This must not happen
+            throw new RuntimeException("Unknown error happened");
+        }
+        if (status == AudioSystem.BAD_VALUE) {
+            throw new IllegalArgumentException(
+                    "attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() "
+                            + "on an unregistered listener");
+        }
+    }
+
+    private <T> int addOnDevRoleForCapturePresetChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull T listener, int deviceRole) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
+        DevRoleListeners<T> devRoleListeners =
+                (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+        if (devRoleListeners == null) {
+            return AudioSystem.ERROR;
+        }
+        synchronized (devRoleListeners.mDevRoleListenersLock) {
+            if (devRoleListeners.hasDevRoleListener(listener)) {
+                return AudioSystem.BAD_VALUE;
+            }
+            // lazy initialization of the list of device role listener
+            if (devRoleListeners.mListenerInfos == null) {
+                devRoleListeners.mListenerInfos = new ArrayList<>();
+            }
+            final int oldCbCount = devRoleListeners.mListenerInfos.size();
+            devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener));
+            if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) {
+                // register binder for callbacks
+                synchronized (mDevRoleForCapturePresetListenersLock) {
+                    int deviceRoleListenerStatus = mDeviceRoleListenersStatus;
+                    mDeviceRoleListenersStatus |= (1 << deviceRole);
+                    if (deviceRoleListenerStatus != 0) {
+                        // There are already device role changed listeners active.
+                        return AudioSystem.SUCCESS;
+                    }
+                    if (mDevicesRoleForCapturePresetDispatcherStub == null) {
+                        mDevicesRoleForCapturePresetDispatcherStub =
+                                new CapturePresetDevicesRoleDispatcherStub();
+                    }
+                    try {
+                        getService().registerCapturePresetDevicesRoleDispatcher(
+                                mDevicesRoleForCapturePresetDispatcherStub);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+        return AudioSystem.SUCCESS;
+    }
+
+    private <T> int removeOnDevRoleForCapturePresetChangedListener(
+            @NonNull T listener, int deviceRole) {
+        Objects.requireNonNull(listener);
+        DevRoleListeners<T> devRoleListeners =
+                (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
+        if (devRoleListeners == null) {
+            return AudioSystem.ERROR;
+        }
+        synchronized (devRoleListeners.mDevRoleListenersLock) {
+            if (!devRoleListeners.removeDevRoleListener(listener)) {
+                return AudioSystem.BAD_VALUE;
+            }
+            if (devRoleListeners.mListenerInfos.size() == 0) {
+                // unregister binder for callbacks
+                synchronized (mDevRoleForCapturePresetListenersLock) {
+                    mDeviceRoleListenersStatus ^= (1 << deviceRole);
+                    if (mDeviceRoleListenersStatus != 0) {
+                        // There are some other device role changed listeners active.
+                        return AudioSystem.SUCCESS;
+                    }
+                    try {
+                        getService().unregisterCapturePresetDevicesRoleDispatcher(
+                                mDevicesRoleForCapturePresetDispatcherStub);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+            }
+        }
+        return AudioSystem.SUCCESS;
+    }
+
+    private final Map<Integer, Object> mDevRoleForCapturePresetListeners = new HashMap<>(){{
+            put(AudioSystem.DEVICE_ROLE_PREFERRED,
+                    new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>());
+        }};
+
+    private class DevRoleListenerInfo<T> {
+        final @NonNull Executor mExecutor;
+        final @NonNull T mListener;
+        DevRoleListenerInfo(Executor executor, T listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+    }
+
+    private class DevRoleListeners<T> {
+        private final Object mDevRoleListenersLock = new Object();
+        @GuardedBy("mDevRoleListenersLock")
+        private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos;
+
+        @GuardedBy("mDevRoleListenersLock")
+        private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) {
+            if (mListenerInfos == null) {
+                return null;
+            }
+            for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) {
+                if (listenerInfo.mListener == listener) {
+                    return listenerInfo;
+                }
+            }
+            return null;
+        }
+
+        @GuardedBy("mDevRoleListenersLock")
+        private boolean hasDevRoleListener(T listener) {
+            return getDevRoleListenerInfo(listener) != null;
+        }
+
+        @GuardedBy("mDevRoleListenersLock")
+        private boolean removeDevRoleListener(T listener) {
+            final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener);
+            if (infoToRemove != null) {
+                mListenerInfos.remove(infoToRemove);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private final Object mDevRoleForCapturePresetListenersLock = new Object();
+    /**
+     * Record if there is a listener added for device role change. If there is a listener added for
+     * a specified device role change, the bit at position `1 << device_role` is set.
+     */
+    @GuardedBy("mDevRoleForCapturePresetListenersLock")
+    private int mDeviceRoleListenersStatus = 0;
+    @GuardedBy("mDevRoleForCapturePresetListenersLock")
+    private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub;
+
+    private final class CapturePresetDevicesRoleDispatcherStub
+            extends ICapturePresetDevicesRoleDispatcher.Stub {
+
+        @Override
+        public void dispatchDevicesRoleChanged(
+                int capturePreset, int role, List<AudioDeviceAttributes> devices) {
+            final Object listenersObj = mDevRoleForCapturePresetListeners.get(role);
+            if (listenersObj == null) {
+                return;
+            }
+            switch (role) {
+                case AudioSystem.DEVICE_ROLE_PREFERRED: {
+                    final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>
+                            listeners =
+                            (DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>)
+                            listenersObj;
+                    final ArrayList<DevRoleListenerInfo<
+                            OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners;
+                    synchronized (listeners.mDevRoleListenersLock) {
+                        if (listeners.mListenerInfos.isEmpty()) {
+                            return;
+                        }
+                        prefDevListeners = (ArrayList<DevRoleListenerInfo<
+                                OnPreferredDevicesForCapturePresetChangedListener>>)
+                                listeners.mListenerInfos.clone();
+                    }
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        for (DevRoleListenerInfo<
+                                OnPreferredDevicesForCapturePresetChangedListener> info :
+                                prefDevListeners) {
+                            info.mExecutor.execute(() ->
+                                    info.mListener.onPreferredDevicesForCapturePresetChanged(
+                                            capturePreset, devices));
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                } break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    //====================================================================
     // Offload query
     /**
      * Returns whether offloaded playback of an audio format is supported on the device.
@@ -6306,6 +6649,132 @@
         }
     }
 
+    /**
+     * Adjusts the volume of the most relevant stream, or the given fallback
+     * stream.
+     * <p>
+     * This method should only be used by applications that replace the
+     * platform-wide management of audio settings or the main telephony
+     * application.
+     * <p>
+     * This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
+     * <p>This API checks if the caller has the necessary permissions based on the provided
+     * component name, uid, and pid values.
+     * See {@link #adjustSuggestedStreamVolume(int, int, int)}.
+     *
+     * @param suggestedStreamType The stream type that will be used if there
+     *         isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
+     *         valid here.
+     * @param direction The direction to adjust the volume. One of
+     *         {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+     *         {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+     *         {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
+     * @param flags One or more flags.
+     * @param packageName the package name of client application
+     * @param uid the uid of client application
+     * @param pid the pid of client application
+     * @param targetSdkVersion the target sdk version of client application
+     * @see #adjustVolume(int, int)
+     * @see #adjustStreamVolume(int, int, int)
+     * @see #setStreamVolume(int, int, int)
+     * @see #isVolumeFixed()
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction, int flags,
+            @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
+        try {
+            getService().adjustSuggestedStreamVolumeForUid(suggestedStreamType, direction, flags,
+                    packageName, uid, pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Adjusts the volume of a particular stream by one step in a direction.
+     * <p>
+     * This method should only be used by applications that replace the platform-wide
+     * management of audio settings or the main telephony application.
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
+     * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
+     * unless the app has been granted Do Not Disturb Access.
+     * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
+     * <p>This API checks if the caller has the necessary permissions based on the provided
+     * component name, uid, and pid values.
+     * See {@link #adjustStreamVolume(int, int, int)}.
+     *
+     * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
+     *         {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
+     *         {@link #STREAM_ALARM} or {@link #STREAM_ACCESSIBILITY}.
+     * @param direction The direction to adjust the volume. One of
+     *         {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
+     *         {@link #ADJUST_SAME}.
+     * @param flags One or more flags.
+     * @param packageName the package name of client application
+     * @param uid the uid of client application
+     * @param pid the pid of client application
+     * @param targetSdkVersion the target sdk version of client application
+     * @see #adjustVolume(int, int)
+     * @see #setStreamVolume(int, int, int)
+     * @throws SecurityException if the adjustment triggers a Do Not Disturb change
+     *         and the caller is not granted notification policy access.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+            @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
+        try {
+            getService().adjustStreamVolumeForUid(streamType, direction, flags, packageName, uid,
+                    pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the volume index for a particular stream.
+     * <p>This method has no effect if the device implements a fixed volume policy
+     * as indicated by {@link #isVolumeFixed()}.
+     * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
+     * the app has been granted Do Not Disturb Access.
+     * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
+     * <p>This API checks if the caller has the necessary permissions based on the provided
+     * component name, uid, and pid values.
+     * See {@link #setStreamVolume(int, int, int)}.
+     *
+     * @param streamType The stream whose volume index should be set.
+     * @param index The volume index to set. See
+     *         {@link #getStreamMaxVolume(int)} for the largest valid value.
+     * @param flags One or more flags.
+     * @param packageName the package name of client application
+     * @param uid the uid of client application
+     * @param pid the pid of client application
+     * @param targetSdkVersion the target sdk version of client application
+     * @see #getStreamMaxVolume(int)
+     * @see #getStreamVolume(int)
+     * @see #isVolumeFixed()
+     * @throws SecurityException if the volume change triggers a Do Not Disturb change
+     *         and the caller is not granted notification policy access.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void setStreamVolumeForUid(int streamType, int index, int flags,
+            @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
+        try {
+            getService().setStreamVolumeForUid(streamType, index, flags, packageName, uid, pid,
+                    UserHandle.getUserHandleForUid(uid), targetSdkVersion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
     /** @hide
      * TODO: make this a @SystemApi */
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index b44d7bb..c827932 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -28,15 +28,6 @@
  */
 public abstract class AudioManagerInternal {
 
-    public abstract void adjustSuggestedStreamVolumeForUid(int streamType, int direction,
-            int flags, String callingPackage, int uid, int pid);
-
-    public abstract void adjustStreamVolumeForUid(int streamType, int direction, int flags,
-            String callingPackage, int uid, int pid);
-
-    public abstract void setStreamVolumeForUid(int streamType, int direction, int flags,
-            String callingPackage, int uid, int pid);
-
     public abstract void setRingerModeDelegate(RingerModeDelegate delegate);
 
     public abstract int getRingerModeInternal();
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 243ec1f..279ba0a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -27,6 +27,7 @@
 import android.media.audiopolicy.AudioMix;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.util.Pair;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -524,6 +525,7 @@
     /** @hide Media server died. see ErrorCallback */
     public static final int AUDIO_STATUS_SERVER_DIED = 100;
 
+    // all accesses must be synchronized (AudioSystem.class)
     private static ErrorCallback sErrorCallback;
 
     /** @hide
@@ -560,11 +562,9 @@
     @UnsupportedAppUsage
     private static void errorCallbackFromNative(int error)
     {
-        ErrorCallback errorCallback = null;
+        ErrorCallback errorCallback;
         synchronized (AudioSystem.class) {
-            if (sErrorCallback != null) {
-                errorCallback = sErrorCallback;
-            }
+            errorCallback = sErrorCallback;
         }
         if (errorCallback != null) {
             errorCallback.onError(error);
@@ -584,6 +584,7 @@
     //keep in sync with include/media/AudioPolicy.h
     private final static int DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE = 0;
 
+    // all accesses must be synchronized (AudioSystem.class)
     private static DynamicPolicyCallback sDynPolicyCallback;
 
     /** @hide */
@@ -598,11 +599,9 @@
     @UnsupportedAppUsage
     private static void dynamicPolicyCallbackFromNative(int event, String regId, int val)
     {
-        DynamicPolicyCallback cb = null;
+        DynamicPolicyCallback cb;
         synchronized (AudioSystem.class) {
-            if (sDynPolicyCallback != null) {
-                cb = sDynPolicyCallback;
-            }
+            cb = sDynPolicyCallback;
         }
         if (cb != null) {
             switch(event) {
@@ -646,6 +645,7 @@
                         int activeSource, String packName);
     }
 
+    // all accesses must be synchronized (AudioSystem.class)
     private static AudioRecordingCallback sRecordingCallback;
 
     /** @hide */
@@ -678,7 +678,7 @@
                           int source, int portId, boolean silenced, int[] recordingFormat,
                           AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects,
                           int activeSource) {
-        AudioRecordingCallback cb = null;
+        AudioRecordingCallback cb;
         synchronized (AudioSystem.class) {
             cb = sRecordingCallback;
         }
@@ -1756,6 +1756,134 @@
     public static native int getDevicesForRoleAndStrategy(
             int strategy, int role, @NonNull List<AudioDeviceAttributes> devices);
 
+    // use case routing by capture preset
+
+    private static Pair<int[], String[]> populateInputDevicesTypeAndAddress(
+            @NonNull List<AudioDeviceAttributes> devices) {
+        int[] types = new int[devices.size()];
+        String[] addresses = new String[devices.size()];
+        for (int i = 0; i < devices.size(); ++i) {
+            types[i] = devices.get(i).getInternalType();
+            if (types[i] == AudioSystem.DEVICE_NONE) {
+                types[i] = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(
+                        devices.get(i).getType());
+            }
+            addresses[i] = devices.get(i).getAddress();
+        }
+        return new Pair<int[], String[]>(types, addresses);
+    }
+
+    /**
+     * @hide
+     * Set devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param devices the list of devices to be set as role for the given capture preset
+     * @return {@link #SUCCESS} if successfully set
+     */
+    public static int setDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+        return setDevicesRoleForCapturePreset(
+                capturePreset, role, typeAddresses.first, typeAddresses.second);
+    }
+
+    /**
+     * @hide
+     * Set devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int setDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Add devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param devices the list of devices to be added as role for the given capture preset
+     * @return {@link #SUCCESS} if successfully add
+     */
+    public static int addDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+        return addDevicesRoleForCapturePreset(
+                capturePreset, role, typeAddresses.first, typeAddresses.second);
+    }
+
+    /**
+     * @hide
+     * Add devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int addDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Remove devices as role for the capture preset
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param devices the devices to be removed
+     * @return {@link #SUCCESS} if successfully removed
+     */
+    public static int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        if (devices.isEmpty()) {
+            return BAD_VALUE;
+        }
+        Pair<int[], String[]> typeAddresses = populateInputDevicesTypeAndAddress(devices);
+        return removeDevicesRoleForCapturePreset(
+                capturePreset, role, typeAddresses.first, typeAddresses.second);
+    }
+
+    /**
+     * @hide
+     * Remove devices as role for capture preset.
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @param types all device types
+     * @param addresses all device addresses
+     * @return {@link #SUCCESS} if successfully set
+     */
+    private static native int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull int[] types, @NonNull String[] addresses);
+
+    /**
+     * @hide
+     * Remove all devices as role for the capture preset
+     * @param capturePreset the capture preset to configure
+     * @param role the role of the devices
+     * @return {@link #SUCCESS} if successfully removed
+     */
+    public static native int clearDevicesRoleForCapturePreset(int capturePreset, int role);
+
+    /**
+     * @hide
+     * Query previously set devices as role for a capture preset
+     * @param capturePreset the capture preset to query for
+     * @param role the role of the devices
+     * @param devices a list that will contain the devices of role
+     * @return {@link #SUCCESS} if there is a preferred device and it was successfully retrieved
+     *     and written to the array
+     */
+    public static native int getDevicesForRoleAndCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices);
+
     // Items shared with audio service
 
     /**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 1c0a526..de2a7b2 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -807,7 +807,8 @@
         int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
                 sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
                 mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
-                offload, encapsulationMode, tunerConfiguration);
+                offload, encapsulationMode, tunerConfiguration,
+                getCurrentOpPackageName());
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing AudioTrack.");
             return; // with mState == STATE_UNINITIALIZED
@@ -893,7 +894,8 @@
                     nativeTrackInJavaObj,
                     false /*offload*/,
                     ENCAPSULATION_MODE_NONE,
-                    null /* tunerConfiguration */);
+                    null /* tunerConfiguration */,
+                    "" /* opPackagename */);
             if (initResult != SUCCESS) {
                 loge("Error code "+initResult+" when initializing AudioTrack.");
                 return; // with mState == STATE_UNINITIALIZED
@@ -4062,7 +4064,8 @@
             Object /*AudioAttributes*/ attributes,
             int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
             int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
-            boolean offload, int encapsulationMode, Object tunerConfiguration);
+            boolean offload, int encapsulationMode, Object tunerConfiguration,
+            @NonNull String opPackageName);
 
     private native final void native_finalize();
 
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ef8b0ed..47e6000 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
 import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioFocusInfo;
@@ -26,6 +27,7 @@
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioServerStateDispatcher;
+import android.media.ICapturePresetDevicesRoleDispatcher;
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
@@ -40,6 +42,7 @@
 import android.media.audiopolicy.IAudioPolicyCallback;
 import android.media.projection.IMediaProjection;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.view.KeyEvent;
 
 /**
@@ -307,4 +310,28 @@
     // code via IAudioManager.h need to be added to the top section.
 
     oneway void setMultiAudioFocusEnabled(in boolean enabled);
+
+    int setPreferredDevicesForCapturePreset(
+            in int capturePreset, in List<AudioDeviceAttributes> devices);
+
+    int clearPreferredDevicesForCapturePreset(in int capturePreset);
+
+    List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(in int capturePreset);
+
+    void registerCapturePresetDevicesRoleDispatcher(ICapturePresetDevicesRoleDispatcher dispatcher);
+
+    oneway void unregisterCapturePresetDevicesRoleDispatcher(
+            ICapturePresetDevicesRoleDispatcher dispatcher);
+
+    oneway void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+            in String packageName, int uid, int pid, in UserHandle userHandle,
+            int targetSdkVersion);
+
+    oneway void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
+            in String packageName, int uid, int pid, in UserHandle userHandle,
+            int targetSdkVersion);
+
+    oneway void setStreamVolumeForUid(int streamType, int direction, int flags,
+            in String packageName, int uid, int pid, in UserHandle userHandle,
+            int targetSdkVersion);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
similarity index 64%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
index 114c30e..5e03e63 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/media/java/android/media/ICapturePresetDevicesRoleDispatcher.aidl
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package android.media;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.media.AudioDeviceAttributes;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+/**
+ * AIDL for AudioService to signal devices role for capture preset updates.
+ *
+ * {@hide}
+ */
+oneway interface ICapturePresetDevicesRoleDispatcher {
 
-import javax.inject.Qualifier;
+    void dispatchDevicesRoleChanged(
+            int capturePreset, int role, in List<AudioDeviceAttributes> devices);
 
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
 }
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index a23191f..523a072 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -15,8 +15,10 @@
  */
 package android.media;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.graphics.Bitmap;
@@ -738,15 +740,16 @@
 
         /**
          * Create a Builder using a {@link MediaMetadata} instance to set
-         * initial values, but replace bitmaps with a scaled down copy if they
-         * are larger than maxBitmapSize.
+         * initial values, but replace bitmaps with a scaled down copy if their width (or height)
+         * is larger than maxBitmapSize.
          *
          * @param source The original metadata to copy.
          * @param maxBitmapSize The maximum height/width for bitmaps contained
          *            in the metadata.
          * @hide
          */
-        public Builder(MediaMetadata source, int maxBitmapSize) {
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        public Builder(@NonNull MediaMetadata source, @IntRange(from = 1) int maxBitmapSize) {
             this(source);
             for (String key : mBundle.keySet()) {
                 Object value = mBundle.get(key);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 49e4160..36ae3ec 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -672,7 +672,8 @@
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
          */
-        native_setup(new WeakReference<MediaPlayer>(this));
+        native_setup(new WeakReference<MediaPlayer>(this),
+                getCurrentOpPackageName());
 
         baseRegisterPlayer();
     }
@@ -2378,7 +2379,7 @@
     private native final int native_setMetadataFilter(Parcel request);
 
     private static native final void native_init();
-    private native final void native_setup(Object mediaplayer_this);
+    private native void native_setup(Object mediaplayerThis, @NonNull String opPackageName);
     private native final void native_finalize();
 
     /**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 4198d79..1db02be 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -404,6 +404,32 @@
         }
     }
 
+    /**
+     * @hide
+     * @param source An audio source to test
+     * @return true if the source is a valid one
+     */
+    public static boolean isValidAudioSource(int source) {
+        switch(source) {
+            case AudioSource.MIC:
+            case AudioSource.VOICE_UPLINK:
+            case AudioSource.VOICE_DOWNLINK:
+            case AudioSource.VOICE_CALL:
+            case AudioSource.CAMCORDER:
+            case AudioSource.VOICE_RECOGNITION:
+            case AudioSource.VOICE_COMMUNICATION:
+            case AudioSource.REMOTE_SUBMIX:
+            case AudioSource.UNPROCESSED:
+            case AudioSource.VOICE_PERFORMANCE:
+            case AudioSource.ECHO_REFERENCE:
+            case AudioSource.RADIO_TUNER:
+            case AudioSource.HOTWORD:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     /** @hide */
     public static final String toLogFriendlyAudioSource(int source) {
         switch(source) {
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 4e2ae5c..451677f 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -18,12 +18,16 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -97,7 +101,9 @@
  TODO(hkuang): Clarify whether supports framerate conversion.
  @hide
  */
-public final class MediaTranscodeManager implements AutoCloseable {
+@TestApi
+@SystemApi
+public final class MediaTranscodeManager {
     private static final String TAG = "MediaTranscodeManager";
 
     private static final String MEDIA_TRANSCODING_SERVICE = "media.transcoding";
@@ -126,25 +132,6 @@
      */
     public static final int TRANSCODING_TYPE_IMAGE = 2;
 
-    @Override
-    public void close() throws Exception {
-        release();
-    }
-
-    /**
-     * Releases the MediaTranscodeManager.
-     */
-    //TODO(hkuang): add test for it.
-    private void release() throws Exception {
-        synchronized (mLock) {
-            if (mTranscodingClient != null) {
-                mTranscodingClient.unregister();
-            } else {
-                throw new UnsupportedOperationException("Failed to release");
-            }
-        }
-    }
-
     /** @hide */
     @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
             TRANSCODING_TYPE_UNKNOWN,
@@ -174,6 +161,7 @@
      * <p>Jobs with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set to
      * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept
      * delay of the transcoding result.
+     * @hide
      * TODO(hkuang): Add more description of this when priority is finalized.
      */
     public static final int PRIORITY_OFFLINE = 2;
@@ -279,7 +267,7 @@
             // Notifies client the progress update.
             if (job.mProgressUpdateExecutor != null && job.mProgressUpdateListener != null) {
                 job.mProgressUpdateExecutor.execute(
-                        () -> job.mProgressUpdateListener.onProgressUpdate(newProgress));
+                        () -> job.mProgressUpdateListener.onProgressUpdate(job, newProgress));
             }
         }
     }
@@ -482,24 +470,17 @@
         throw new UnsupportedOperationException("Failed to register new client");
     }
 
-    /* Private constructor. */
-    private MediaTranscodeManager(@NonNull Context context,
-            IMediaTranscodingService transcodingService) {
+    /**
+     * @hide
+     */
+    public MediaTranscodeManager(@NonNull Context context) {
         mContext = context;
         mContentResolver = mContext.getContentResolver();
         mPackageName = mContext.getPackageName();
         mPid = Os.getuid();
         mUid = Os.getpid();
-        mTranscodingClient = registerClient(transcodingService);
-    }
-
-    @Override
-    protected void finalize() {
-        try {
-            release();
-        } catch (Exception ex) {
-            Log.e(TAG, "Failed to release");
-        }
+        IMediaTranscodingService service = getService(false /*retry*/);
+        mTranscodingClient = registerClient(service);
     }
 
     public static final class TranscodingRequest {
@@ -555,25 +536,25 @@
 
         /** Return the type of the transcoding. */
         @TranscodingType
-        int getType() {
+        public int getType() {
             return mType;
         }
 
         /** Return source uri of the transcoding. */
         @NonNull
-        Uri getSourceUri() {
+        public Uri getSourceUri() {
             return mSourceUri;
         }
 
         /** Return destination uri of the transcoding. */
         @NonNull
-        Uri getDestinationUri() {
+        public Uri getDestinationUri() {
             return mDestinationUri;
         }
 
         /** Return priority of the transcoding. */
         @TranscodingPriority
-        int getPriority() {
+        public int getPriority() {
             return mPriority;
         }
 
@@ -581,10 +562,20 @@
          * Return the video track format of the transcoding.
          * This will be null is the transcoding is not for video transcoding.
          */
-        MediaFormat getVideoTrackFormat() {
+        @Nullable
+        public MediaFormat getVideoTrackFormat() {
             return mVideoTrackFormat;
         }
 
+        /**
+         * Return TestConfig of the transcoding.
+         * @hide
+         */
+        @Nullable
+        public TranscodingTestConfig getTestConfig() {
+            return mTestConfig;
+        }
+
         /* Writes the TranscodingRequest to a parcel. */
         private TranscodingRequestParcel writeToParcel() {
             TranscodingRequestParcel parcel = new TranscodingRequestParcel();
@@ -665,7 +656,7 @@
          * Builder class for {@link TranscodingRequest} objects.
          * Use this class to configure and create a <code>TranscodingRequest</code> instance.
          */
-        public static class Builder {
+        public static final class Builder {
             private @NonNull Uri mSourceUri;
             private @NonNull Uri mDestinationUri;
             private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
@@ -684,7 +675,7 @@
              */
             // TODO(hkuang): Add documentation on how the app could generate the correct Uri.
             @NonNull
-            public Builder setSourceUri(@NonNull Uri sourceUri) throws IllegalArgumentException {
+            public Builder setSourceUri(@NonNull Uri sourceUri) {
                 if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) {
                     throw new IllegalArgumentException(
                             "You must specify a non-empty source Uri.");
@@ -701,8 +692,7 @@
              * @throws IllegalArgumentException if Uri is null or empty.
              */
             @NonNull
-            public Builder setDestinationUri(@NonNull Uri destinationUri)
-                    throws IllegalArgumentException {
+            public Builder setDestinationUri(@NonNull Uri destinationUri) {
                 if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) {
                     throw new IllegalArgumentException(
                             "You must specify a non-empty destination Uri.");
@@ -719,8 +709,7 @@
              * @throws IllegalArgumentException if flags is invalid.
              */
             @NonNull
-            public Builder setPriority(@TranscodingPriority int priority)
-                    throws IllegalArgumentException {
+            public Builder setPriority(@TranscodingPriority int priority) {
                 if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) {
                     throw new IllegalArgumentException("Invalid priority: " + priority);
                 }
@@ -738,8 +727,7 @@
              * @throws IllegalArgumentException if flags is invalid.
              */
             @NonNull
-            public Builder setType(@TranscodingType int type)
-                    throws IllegalArgumentException {
+            public Builder setType(@TranscodingType int type) {
                 if (type != TRANSCODING_TYPE_VIDEO && type != TRANSCODING_TYPE_IMAGE) {
                     throw new IllegalArgumentException("Invalid transcoding type");
                 }
@@ -763,8 +751,7 @@
              * @throws IllegalArgumentException if videoFormat is invalid.
              */
             @NonNull
-            public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat)
-                    throws IllegalArgumentException {
+            public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat) {
                 if (videoFormat == null) {
                     throw new IllegalArgumentException("videoFormat must not be null");
                 }
@@ -784,9 +771,11 @@
              * Sets the delay in processing this request.
              * @param config test config.
              * @return The same builder instance.
+             * @hide
              */
             @VisibleForTesting
-            public Builder setTestConfig(TranscodingTestConfig config) {
+            @NonNull
+            public Builder setTestConfig(@NonNull TranscodingTestConfig config) {
                 mTestConfig = config;
                 return this;
             }
@@ -799,7 +788,7 @@
              *         device.
              */
             @NonNull
-            public TranscodingRequest build() throws UnsupportedOperationException {
+            public TranscodingRequest build() {
                 if (mSourceUri == null) {
                     throw new UnsupportedOperationException("Source URI must not be null");
                 }
@@ -826,6 +815,141 @@
                 return new TranscodingRequest(this);
             }
         }
+
+        /**
+         * Helper class for deciding if transcoding is needed, and if so, the track
+         * formats to use.
+         */
+        public static class MediaFormatResolver {
+            private static final int BIT_RATE = 20000000;            // 20Mbps
+
+            private MediaFormat mSrcVideoFormatHint;
+            private MediaFormat mSrcAudioFormatHint;
+            private Bundle mClientCaps;
+
+            /**
+             * A key describing whether the client supports HEVC-encoded video.
+             *
+             * The value associated with this key is a boolean. If unspecified, it's up to
+             * the MediaFormatResolver to determine the default.
+             *
+             * @see #setClientCapabilities(Bundle)
+             */
+            public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+
+            /**
+             * Sets the abilities of the client consuming the media. Must be called
+             * before {@link #shouldTranscode()} or {@link #resolveVideoFormat()}.
+             *
+             * @param clientCaps A Bundle object containing the client's capabilities, such as
+             *                   {@link #CAPS_SUPPORTS_HEVC}.
+             * @return the same VideoFormatResolver instance.
+             * @hide
+             */
+            @NonNull
+            public MediaFormatResolver setClientCapabilities(@NonNull Bundle clientCaps) {
+                mClientCaps = clientCaps;
+                return this;
+            }
+
+            /**
+             * Sets the video format hint about the source. Must be called before
+             * {@link #shouldTranscode()} or {@link #resolveVideoFormat()}.
+             *
+             * @param format A MediaFormat object containing information about the source's
+             *               video track format that could affect the transcoding decision.
+             *               Such information could include video codec types, color spaces,
+             *               whether special format info (eg. slow-motion markers) are present,
+             *               etc.. If a particular information is not present, it will not be
+             *               used to make the decision.
+             * @return the same MediaFormatResolver instance.
+             */
+            @NonNull
+            public MediaFormatResolver setSourceVideoFormatHint(@NonNull MediaFormat format) {
+                mSrcVideoFormatHint = format;
+                return this;
+            }
+
+            /**
+             * Sets the audio format hint about the source.
+             *
+             * @param format A MediaFormat object containing information about the source's
+             *               audio track format that could affect the transcoding decision.
+             * @return the same MediaFormatResolver instance.
+             * @hide
+             */
+            @NonNull
+            public MediaFormatResolver setSourceAudioFormatHint(@NonNull MediaFormat format) {
+                mSrcAudioFormatHint = format;
+                return this;
+            }
+
+            /**
+             * Returns whether the source content should be transcoded.
+             *
+             * @return true if the source should be transcoded.
+             * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)}
+             *         or {@link #setSourceVideoFormatHint(MediaFormat)} was not called.
+             */
+            public boolean shouldTranscode() {
+                if (mClientCaps == null) {
+                    throw new UnsupportedOperationException(
+                            "Client caps must be set!");
+                }
+                // Video src hint must be provided, audio src hint is not used right now.
+                if (mSrcVideoFormatHint == null) {
+                    throw new UnsupportedOperationException(
+                            "Source video format hint must be set!");
+                }
+                boolean supportHevc = mClientCaps.getBoolean(CAPS_SUPPORTS_HEVC, false);
+                if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals(
+                        mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) {
+                    return true;
+                }
+                // TODO: add more checks as needed below.
+                return false;
+            }
+
+            /**
+             * Retrieves the video track format to be used on
+             * {@link Builder#setVideoTrackFormat(MediaFormat)} for this configuration.
+             *
+             * @return the video track format to be used if transcoding should be performed,
+             *         and null otherwise.
+             * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)}
+             *         or {@link #setSourceVideoFormatHint(MediaFormat)} was not called.
+             */
+            @Nullable
+            public MediaFormat resolveVideoFormat() {
+                if (!shouldTranscode()) {
+                    return null;
+                }
+                // TODO(hkuang): Only modified the video codec type, and use fixed bitrate for now.
+                // May switch to transcoding profile when it's available.
+                MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint);
+                videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
+                videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+                return videoTrackFormat;
+            }
+
+            /**
+             * Retrieves the audio track format to be used for transcoding.
+             *
+             * @return the audio track format to be used if transcoding should be performed, and
+             *         null otherwise.
+             * @throws UnsupportedOperationException if {@link #setClientCapabilities(Bundle)}
+             *         or {@link #setSourceVideoFormatHint(MediaFormat)} was not called.
+             * @hide
+             */
+            @Nullable
+            public MediaFormat resolveAudioFormat() {
+                if (!shouldTranscode()) {
+                    return null;
+                }
+                // Audio transcoding is not supported yet, always return null.
+                return null;
+            }
+        }
     }
 
     /**
@@ -843,6 +967,7 @@
         /** The job is paused. */
         public static final int STATUS_PAUSED = 4;
 
+        /** @hide */
         @IntDef(prefix = { "STATUS_" }, value = {
                 STATUS_PENDING,
                 STATUS_RUNNING,
@@ -861,6 +986,7 @@
         /** The job was canceled by the caller. */
         public static final int RESULT_CANCELED = 4;
 
+        /** @hide */
         @IntDef(prefix = { "RESULT_" }, value = {
                 RESULT_NONE,
                 RESULT_SUCCESS,
@@ -876,26 +1002,31 @@
             /**
              * Called when the progress changes. The progress is in percentage between 0 and 1,
              * where 0 means that the job has not yet started and 100 means that it has finished.
+             *
+             * @param job      The job associated with the progress.
              * @param progress The new progress ranging from 0 ~ 100 inclusive.
              */
-            void onProgressUpdate(int progress);
+            void onProgressUpdate(@NonNull TranscodingJob job,
+                    @IntRange(from = 0, to = 100) int progress);
         }
 
         private final ITranscodingClient mJobOwner;
         private final Executor mListenerExecutor;
         private final OnTranscodingFinishedListener mListener;
         private int mJobId = -1;
-        @GuardedBy("this")
+        // Lock for internal state.
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
         private Executor mProgressUpdateExecutor = null;
-        @GuardedBy("this")
+        @GuardedBy("mLock")
         private OnProgressUpdateListener mProgressUpdateListener = null;
-        @GuardedBy("this")
+        @GuardedBy("mLock")
         private int mProgress = 0;
-        @GuardedBy("this")
+        @GuardedBy("mLock")
         private int mProgressUpdateInterval = 0;
-        @GuardedBy("this")
+        @GuardedBy("mLock")
         private @Status int mStatus = STATUS_PENDING;
-        @GuardedBy("this")
+        @GuardedBy("mLock")
         private @Result int mResult = RESULT_NONE;
 
         private TranscodingJob(
@@ -932,20 +1063,24 @@
          * @param executor The executor on which listener will be invoked.
          * @param listener The progress listener.
          */
-        public synchronized void setOnProgressUpdateListener(
+        public void setOnProgressUpdateListener(
                 int minProgressUpdateInterval,
                 @NonNull @CallbackExecutor Executor executor,
                 @Nullable OnProgressUpdateListener listener) {
-            Objects.requireNonNull(executor, "listenerExecutor must not be null");
-            Objects.requireNonNull(listener, "listener must not be null");
-            mProgressUpdateExecutor = executor;
-            mProgressUpdateListener = listener;
+            synchronized (mLock) {
+                Objects.requireNonNull(executor, "listenerExecutor must not be null");
+                Objects.requireNonNull(listener, "listener must not be null");
+                mProgressUpdateExecutor = executor;
+                mProgressUpdateListener = listener;
+            }
         }
 
-        private synchronized void updateStatusAndResult(@Status int jobStatus,
+        private void updateStatusAndResult(@Status int jobStatus,
                 @Result int jobResult) {
-            mStatus = jobStatus;
-            mResult = jobResult;
+            synchronized (mLock) {
+                mStatus = jobStatus;
+                mResult = jobResult;
+            }
         }
 
         /**
@@ -953,8 +1088,10 @@
          *
          * @return true if successfully resubmit the job to the service. False otherwise.
          */
-        public synchronized boolean retry() {
-            // TODO(hkuang): Implement this.
+        public boolean retry() {
+            synchronized (mLock) {
+                // TODO(hkuang): Implement this.
+            }
             return true;
         }
 
@@ -963,38 +1100,46 @@
          * If the job happened to finish before being canceled this call is effectively a no-op and
          * will not update the result in that case.
          */
-        public synchronized void cancel() {
-            // Check if the job is finished already.
-            if (mStatus != STATUS_FINISHED) {
-                try {
-                    mJobOwner.cancelJob(mJobId);
-                } catch (RemoteException re) {
-                    //TODO(hkuang): Find out what to do if failing to cancel the job.
-                    Log.e(TAG, "Failed to cancel the job due to exception:  " + re);
-                }
-                mStatus = STATUS_FINISHED;
-                mResult = RESULT_CANCELED;
+        public void cancel() {
+            synchronized (mLock) {
+                // Check if the job is finished already.
+                if (mStatus != STATUS_FINISHED) {
+                    try {
+                        mJobOwner.cancelJob(mJobId);
+                    } catch (RemoteException re) {
+                        //TODO(hkuang): Find out what to do if failing to cancel the job.
+                        Log.e(TAG, "Failed to cancel the job due to exception:  " + re);
+                    }
+                    mStatus = STATUS_FINISHED;
+                    mResult = RESULT_CANCELED;
 
-                // Notifies client the job is canceled.
-                mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this));
+                    // Notifies client the job is canceled.
+                    mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this));
+                }
             }
         }
 
         /**
-         * Gets the progress of the transcoding job. The progress is between 0 and 1, where 0 means
-         * that the job has not yet started and 1 means that it is finished.
+         * Gets the progress of the transcoding job. The progress is between 0 and 100, where 0
+         * means that the job has not yet started and 100 means that it is finished. For the
+         * cancelled job, the progress will be the last updated progress before it is cancelled.
          * @return The progress.
          */
-        public synchronized int getProgress() {
-            return mProgress;
+        @IntRange(from = 0, to = 100)
+        public int getProgress() {
+            synchronized (mLock) {
+                return mProgress;
+            }
         }
 
         /**
          * Gets the status of the transcoding job.
          * @return The status.
          */
-        public synchronized @Status int getStatus() {
-            return mStatus;
+        public @Status int getStatus() {
+            synchronized (mLock) {
+                return mStatus;
+            }
         }
 
         /**
@@ -1009,53 +1154,22 @@
          * Gets the result of the transcoding job.
          * @return The result.
          */
-        public synchronized @Result int getResult() {
-            return mResult;
-        }
-
-        private synchronized void updateProgress(int newProgress) {
-            mProgress = newProgress;
-        }
-
-        private synchronized void updateStatus(int newStatus) {
-            mStatus = newStatus;
-        }
-    }
-
-    /**
-     * Gets the MediaTranscodeManager singleton instance.
-     *
-     * @param context The application context.
-     * @return the {@link MediaTranscodeManager} singleton instance.
-     * @throws UnsupportedOperationException if failing to acquire the MediaTranscodeManager.
-     */
-    public static MediaTranscodeManager getInstance(@NonNull Context context) {
-        // Acquires the MediaTranscoding service.
-        IMediaTranscodingService service = getService(false /*retry*/);
-        return getInstance(context, service);
-    }
-
-    /** Similar as above, but wait till the service is ready. */
-    @VisibleForTesting
-    public static MediaTranscodeManager getInstance(@NonNull Context context, boolean retry) {
-        // Acquires the MediaTranscoding service.
-        IMediaTranscodingService service = getService(retry);
-        return getInstance(context, service);
-    }
-
-    /** Similar as above, but allow injecting transcodingService for testing. */
-    @VisibleForTesting
-    public static MediaTranscodeManager getInstance(@NonNull Context context,
-            IMediaTranscodingService transcodingService) {
-        Objects.requireNonNull(context, "context must not be null");
-
-        synchronized (MediaTranscodeManager.class) {
-            if (sMediaTranscodeManager == null) {
-                sMediaTranscodeManager = new MediaTranscodeManager(context.getApplicationContext(),
-                        transcodingService);
+        public @Result int getResult() {
+            synchronized (mLock) {
+                return mResult;
             }
+        }
 
-            return sMediaTranscodeManager;
+        private void updateProgress(int newProgress) {
+            synchronized (mLock) {
+                mProgress = newProgress;
+            }
+        }
+
+        private void updateStatus(int newStatus) {
+            synchronized (mLock) {
+                mStatus = newStatus;
+            }
         }
     }
 
@@ -1077,7 +1191,7 @@
             @NonNull TranscodingRequest transcodingRequest,
             @NonNull @CallbackExecutor Executor listenerExecutor,
             @NonNull OnTranscodingFinishedListener listener)
-            throws UnsupportedOperationException, FileNotFoundException {
+            throws FileNotFoundException {
         Log.i(TAG, "enqueueRequest called.");
         Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null");
         Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null");
@@ -1092,8 +1206,14 @@
             // Synchronizes the access to mPendingTranscodingJobs to make sure the job Id is
             // inserted in the mPendingTranscodingJobs in the callback handler.
             synchronized (mPendingTranscodingJobs) {
-                if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
-                    throw new UnsupportedOperationException("Failed to enqueue request");
+                synchronized (mLock) {
+                    if (mTranscodingClient == null) {
+                        // TODO(hkuang): Handle the case if client is temporarily unavailable.
+                    }
+
+                    if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
+                        throw new UnsupportedOperationException("Failed to enqueue request");
+                    }
                 }
 
                 // Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index ee8f1b3..df5e85e 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -27,6 +27,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -622,4 +623,8 @@
         Log.w(className, "See the documentation of " + opName + " for what to use instead with " +
                 "android.media.AudioAttributes to qualify your playback use case");
     }
+
+    protected String getCurrentOpPackageName() {
+        return TextUtils.emptyIfNull(ActivityThread.currentOpPackageName());
+    }
 }
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index aa57233..24dacc4 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -16,8 +16,11 @@
 
 package android.media.session;
 
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -27,6 +30,7 @@
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
+import android.media.VolumeProvider.ControlType;
 import android.media.session.MediaSession.QueueItem;
 import android.net.Uri;
 import android.os.Bundle;
@@ -41,6 +45,8 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
@@ -950,6 +956,14 @@
      * this session.
      */
     public static final class PlaybackInfo implements Parcelable {
+
+        /**
+         * @hide
+         */
+        @IntDef({PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface PlaybackType {}
+
         /**
          * The session uses local playback.
          */
@@ -959,7 +973,7 @@
          */
         public static final int PLAYBACK_TYPE_REMOTE = 2;
 
-        private final int mVolumeType;
+        private final int mPlaybackType;
         private final int mVolumeControl;
         private final int mMaxVolume;
         private final int mCurrentVolume;
@@ -967,27 +981,35 @@
         private final String mVolumeControlId;
 
         /**
+         * Creates a new playback info.
+         *
+         * @param playbackType The playback type. Should be {@link #PLAYBACK_TYPE_LOCAL} or
+         *                     {@link #PLAYBACK_TYPE_REMOTE}
+         * @param volumeControl The volume control. Should be one of:
+         *                      {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE},
+         *                      {@link VolumeProvider#VOLUME_CONTROL_RELATIVE}, and
+         *                      {@link VolumeProvider#VOLUME_CONTROL_FIXED}.
+         * @param maxVolume The max volume. Should be equal or greater than zero.
+         * @param currentVolume The current volume. Should be in the interval [0, maxVolume].
+         * @param audioAttrs The audio attributes for this playback. Should not be null.
+         * @param volumeControlId The volume control ID. This is used for matching
+         *                        {@link RoutingSessionInfo} and {@link MediaSession}.
          * @hide
          */
-        public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) {
-            this(type, control, max, current, attrs, null);
-        }
-
-        /**
-         * @hide
-         */
-        public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs,
-                String volumeControlId) {
-            mVolumeType = type;
-            mVolumeControl = control;
-            mMaxVolume = max;
-            mCurrentVolume = current;
-            mAudioAttrs = attrs;
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        public PlaybackInfo(@PlaybackType int playbackType, @ControlType int volumeControl,
+                @IntRange(from = 0) int maxVolume, @IntRange(from = 0) int currentVolume,
+                @NonNull AudioAttributes audioAttrs, @Nullable String volumeControlId) {
+            mPlaybackType = playbackType;
+            mVolumeControl = volumeControl;
+            mMaxVolume = maxVolume;
+            mCurrentVolume = currentVolume;
+            mAudioAttrs = audioAttrs;
             mVolumeControlId = volumeControlId;
         }
 
         PlaybackInfo(Parcel in) {
-            mVolumeType = in.readInt();
+            mPlaybackType = in.readInt();
             mVolumeControl = in.readInt();
             mMaxVolume = in.readInt();
             mCurrentVolume = in.readInt();
@@ -1005,7 +1027,7 @@
          * @return The type of playback this session is using.
          */
         public int getPlaybackType() {
-            return mVolumeType;
+            return mPlaybackType;
         }
 
         /**
@@ -1016,8 +1038,7 @@
          * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
          * </ul>
          *
-         * @return The type of volume control that may be used with this
-         *         session.
+         * @return The type of volume control that may be used with this session.
          */
         public int getVolumeControl() {
             return mVolumeControl;
@@ -1075,7 +1096,7 @@
 
         @Override
         public String toString() {
-            return "volumeType=" + mVolumeType + ", volumeControl=" + mVolumeControl
+            return "playbackType=" + mPlaybackType + ", volumeControlType=" + mVolumeControl
                     + ", maxVolume=" + mMaxVolume + ", currentVolume=" + mCurrentVolume
                     + ", audioAttrs=" + mAudioAttrs + ", volumeControlId=" + mVolumeControlId;
         }
@@ -1087,7 +1108,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mVolumeType);
+            dest.writeInt(mPlaybackType);
             dest.writeInt(mVolumeControl);
             dest.writeInt(mMaxVolume);
             dest.writeInt(mCurrentVolume);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 7518496..1e9cf2c 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -166,26 +166,3 @@
     ],
 }
 
-cc_library_shared {
-    name: "libmediatranscodemanager_jni",
-    srcs: [
-        "android_media_MediaTranscodeManager.cpp",
-    ],
-
-    shared_libs: [
-        "libandroid_runtime",
-        "libcutils",
-        "liblog",
-        "libmedia",
-    ],
-
-    export_include_dirs: ["."],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-error=deprecated-declarations",
-        "-Wunused",
-        "-Wunreachable-code",
-    ],
-}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 55aac09..bd8d2e9 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -33,6 +33,7 @@
 #include <utils/threads.h>
 #include "jni.h"
 #include <nativehelper/JNIPlatformHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
 #include "android_runtime/Log.h"
@@ -944,10 +945,12 @@
 }
 
 static void
-android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
+android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+                                       jstring opPackageName)
 {
     ALOGV("native_setup");
-    sp<MediaPlayer> mp = new MediaPlayer();
+    ScopedUtfChars opPackageNameStr(env, opPackageName);
+    sp<MediaPlayer> mp = new MediaPlayer(opPackageNameStr.c_str());
     if (mp == NULL) {
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
@@ -1403,7 +1406,7 @@
     {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
     {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
     {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
-    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
+    {"native_setup",        "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
     {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
diff --git a/media/jni/android_media_MediaTranscodeManager.cpp b/media/jni/android_media_MediaTranscodeManager.cpp
deleted file mode 100644
index 6695f85..0000000
--- a/media/jni/android_media_MediaTranscodeManager.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaTranscodeManager_JNI"
-
-#include "android_runtime/AndroidRuntime.h"
-#include "jni.h"
-
-#include <nativehelper/JNIHelp.h>
-#include <utils/Log.h>
-
-namespace {
-
-// NOTE: Keep these enums in sync with their equivalents in MediaTranscodeManager.java.
-enum {
-    ID_INVALID = -1
-};
-
-enum {
-    EVENT_JOB_STARTED = 1,
-    EVENT_JOB_PROGRESSED = 2,
-    EVENT_JOB_FINISHED = 3,
-};
-
-enum {
-    RESULT_NONE = 1,
-    RESULT_SUCCESS = 2,
-    RESULT_ERROR = 3,
-    RESULT_CANCELED = 4,
-};
-
-struct {
-    jmethodID postEventFromNative;
-} gMediaTranscodeManagerClassInfo;
-
-using namespace android;
-
-void android_media_MediaTranscodeManager_native_init(JNIEnv *env, jclass clazz) {
-    ALOGV("android_media_MediaTranscodeManager_native_init");
-
-    gMediaTranscodeManagerClassInfo.postEventFromNative = env->GetMethodID(
-            clazz, "postEventFromNative", "(IJI)V");
-    LOG_ALWAYS_FATAL_IF(gMediaTranscodeManagerClassInfo.postEventFromNative == NULL,
-                        "can't find android/media/MediaTranscodeManager.postEventFromNative");
-}
-
-jlong android_media_MediaTranscodeManager_requestUniqueJobID(
-        JNIEnv *env __unused, jobject thiz __unused) {
-    ALOGV("android_media_MediaTranscodeManager_reserveUniqueJobID");
-    static std::atomic_int32_t sJobIDCounter{0};
-    jlong id = (jlong)++sJobIDCounter;
-    return id;
-}
-
-jboolean android_media_MediaTranscodeManager_enqueueTranscodingRequest(
-        JNIEnv *env, jobject thiz, jlong id, jobject request, jobject context __unused) {
-    ALOGV("android_media_MediaTranscodeManager_enqueueTranscodingRequest");
-    if (!request) {
-        return ID_INVALID;
-    }
-
-    env->CallVoidMethod(thiz, gMediaTranscodeManagerClassInfo.postEventFromNative,
-                        EVENT_JOB_FINISHED, id, RESULT_ERROR);
-    return true;
-}
-
-void android_media_MediaTranscodeManager_cancelTranscodingRequest(
-        JNIEnv *env __unused, jobject thiz __unused, jlong jobID __unused) {
-    ALOGV("android_media_MediaTranscodeManager_cancelTranscodingRequest");
-}
-
-const JNINativeMethod gMethods[] = {
-    { "native_init", "()V",
-        (void *)android_media_MediaTranscodeManager_native_init },
-    { "native_requestUniqueJobID", "()J",
-        (void *)android_media_MediaTranscodeManager_requestUniqueJobID },
-    { "native_enqueueTranscodingRequest",
-        "(JLandroid/media/MediaTranscodeManager$TranscodingRequest;Landroid/content/Context;)Z",
-        (void *)android_media_MediaTranscodeManager_enqueueTranscodingRequest },
-    { "native_cancelTranscodingRequest", "(J)V",
-        (void *)android_media_MediaTranscodeManager_cancelTranscodingRequest },
-};
-
-} // namespace anonymous
-
-int register_android_media_MediaTranscodeManager(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(env,
-                "android/media/MediaTranscodeManager", gMethods, NELEM(gMethods));
-}
-
-jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
-{
-    JNIEnv* env = NULL;
-    jint result = -1;
-
-    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-        ALOGE("ERROR: GetEnv failed\n");
-        return result;
-    }
-    assert(env != NULL);
-
-    if (register_android_media_MediaTranscodeManager(env) < 0) {
-        ALOGE("ERROR: MediaTranscodeManager native registration failed");
-        goto bail;
-    }
-
-    /* success -- return valid version number */
-    result = JNI_VERSION_1_4;
-
-bail:
-    return result;
-}
diff --git a/media/packages/BluetoothMidiService/Android.bp b/media/packages/BluetoothMidiService/Android.bp
index 48fc329..25c34c3 100644
--- a/media/packages/BluetoothMidiService/Android.bp
+++ b/media/packages/BluetoothMidiService/Android.bp
@@ -29,6 +29,5 @@
         "src/**/*.java",
     ],
     platform_apis: true,
-    certificate: "platform",
     manifest: "AndroidManifest.xml",
 }
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index b88bf2a..fc96fd9 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -19,8 +19,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:tools="http://schemas.android.com/tools"
         package="com.android.bluetoothmidiservice"
-        android:versionCode="1"
-        android:versionName="R-initial"
         >
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
 
diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
index ebe62b0..bfb0546 100644
--- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
@@ -18,8 +18,6 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.bluetoothmidiservice"
-          android:versionCode="1"
-          android:versionName="R-initial"
           >
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
     <application
diff --git a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
index 43db353..c8fb3a6 100644
--- a/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
+++ b/media/tests/MediaTranscodingTest/build_and_run_unit_tests.sh
@@ -31,6 +31,9 @@
 adb push --sync $file /data/user/0/com.android.mediatranscodingtest/cache/
 done
 
-echo "[==========] running real transcoding tests"
+echo "[==========] running MediaTranscodeManagerTest"
 adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
 
+echo "[==========] running MediaTranscodeManagerDiedTest"
+adb shell am instrument -e class com.android.mediatranscodingtest.MediaTranscodeManagerDiedTest -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
new file mode 100644
index 0000000..f00c14d
--- /dev/null
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2020 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.mediatranscodingtest;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.MediaFormat;
+import android.media.MediaTranscodeManager;
+import android.media.MediaTranscodeManager.TranscodingJob;
+import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/*
+ * Service died tests for MediaTranscodeManager in the media framework.
+ *
+ * To run this test suite:
+     make frameworks/base/media/tests/MediaTranscodingTest
+     make mediatranscodingtest
+
+     adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
+
+     adb shell am instrument -e class \
+     com.android.mediatranscodingtest.MediaTranscodeManagerDiedTest \
+     -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
+ *
+ */
+public class MediaTranscodeManagerDiedTest
+        extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
+    private static final String TAG = "MediaTranscodeManagerDiedTest";
+    /** The time to wait for the transcode operation to complete before failing the test. */
+    private static final int TRANSCODE_TIMEOUT_SECONDS = 10;
+
+    /** Maximum number of retry to connect to the service. */
+    private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
+
+    /** Interval between trying to reconnect to the service. */
+    private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40;
+
+    private Context mContext;
+    private MediaTranscodeManager mMediaTranscodeManager = null;
+    private Uri mSourceHEVCVideoUri = null;
+    private Uri mSourceAVCVideoUri = null;
+    private Uri mDestinationUri = null;
+
+    // Setting for transcoding to H.264.
+    private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
+    private static final int BIT_RATE = 20000000;            // 20Mbps
+    private static final int WIDTH = 1920;
+    private static final int HEIGHT = 1080;
+
+    public MediaTranscodeManagerDiedTest() {
+        super("com.android.MediaTranscodeManagerTest", MediaTranscodingTest.class);
+    }
+
+    // Copy the resource to cache.
+    private Uri resourceToUri(Context context, int resId, String name) throws IOException {
+        Uri resUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+                .authority(context.getResources().getResourcePackageName(resId))
+                .appendPath(context.getResources().getResourceTypeName(resId))
+                .appendPath(context.getResources().getResourceEntryName(resId))
+                .build();
+
+        Uri cacheUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+                + mContext.getCacheDir().getAbsolutePath() + "/" + name);
+
+        InputStream is = mContext.getResources().openRawResource(resId);
+        OutputStream os = mContext.getContentResolver().openOutputStream(cacheUri);
+
+        FileUtils.copy(is, os);
+
+        return cacheUri;
+    }
+
+    private static Uri generateNewUri(Context context, String filename) {
+        File outFile = new File(context.getExternalCacheDir(), filename);
+        return Uri.fromFile(outFile);
+    }
+
+    /**
+     * Creates a MediaFormat with the basic set of values.
+     */
+    private static MediaFormat createMediaFormat() {
+        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+        return format;
+    }
+
+    private MediaTranscodeManager getManager() {
+        for (int count = 1;  count <= CONNECT_SERVICE_RETRY_COUNT; count++) {
+            Log.d(TAG, "Trying to connect to service. Try count: " + count);
+            MediaTranscodeManager manager = mContext.getSystemService(MediaTranscodeManager.class);
+            if (manager != null) {
+                return manager;
+            }
+            try {
+                // Sleep a bit before retry.
+                Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+
+        throw new UnsupportedOperationException("Failed to acquire MediaTranscodeManager");
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        Log.d(TAG, "setUp");
+        super.setUp();
+
+        mContext = getInstrumentation().getContext();
+        mMediaTranscodeManager = getManager();
+        assertNotNull(mMediaTranscodeManager);
+        androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
+
+        // Setup source HEVC file uri.
+        mSourceHEVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyHEVC, "VideoOnlyHEVC.mp4");
+
+        // Setup source AVC file uri.
+        mSourceAVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyAVC,
+                "VideoOnlyAVC.mp4");
+
+        // Setup destination file.
+        mDestinationUri = generateNewUri(mContext, "transcoded.mp4");
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    // [[ $(adb shell whoami) == "root" ]]
+    private boolean checkIfRoot() {
+        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation()
+                .executeShellCommand("whoami");
+             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                     new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                if (line.contains("root")) {
+                    return true;
+                }
+            }
+        } catch (IOException ie) {
+            return false;
+        }
+        return false;
+    }
+
+    private String executeShellCommand(String cmd) throws Exception {
+        return UiDevice.getInstance(
+                InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+    }
+
+    @Test
+    public void testHandleTranscoderServiceDied() throws Exception {
+        if (!checkIfRoot()) {
+            throw new AssertionError("must be root to run this test; try adb root?");
+        }
+
+        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+        Semaphore jobStartedSemaphore = new Semaphore(0);
+
+        // Transcode a 15 seconds video, so that the transcoding is not finished when we kill the
+        // service.
+        Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+                + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4");
+        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+                + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
+
+        TranscodingRequest request =
+                new TranscodingRequest.Builder()
+                        .setSourceUri(mSourceHEVCVideoUri)
+                        .setDestinationUri(destinationUri)
+                        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                        .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                        .setVideoTrackFormat(createMediaFormat())
+                        .build();
+        Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+        Log.i(TAG, "transcoding to " + createMediaFormat());
+
+        TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+                transcodingJob -> {
+                    Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+                    transcodeCompleteSemaphore.release();
+                });
+        assertNotNull(job);
+
+        AtomicInteger progressUpdateCount = new AtomicInteger(0);
+
+        // Set progress update executor and use the same executor as result listener.
+        job.setOnProgressUpdateListener(listenerExecutor,
+                new TranscodingJob.OnProgressUpdateListener() {
+                    @Override
+                    public void onProgressUpdate(TranscodingJob job, int newProgress) {
+                        if (newProgress > 0) {
+                            jobStartedSemaphore.release();
+                        }
+                    }
+                });
+
+        // Wait for progress update so the job is in running state.
+        jobStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue("Job is not running", job.getStatus() == TranscodingJob.STATUS_RUNNING);
+
+        // Kills the service and expects receiving failure of the job.
+        executeShellCommand("pkill -f media.transcoding");
+
+        Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode result.");
+        boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+                TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
+        assertTrue("Invalid job result", job.getResult()== TranscodingJob.RESULT_ERROR);
+    }
+}
+
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 009a41e..33d6d64 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -16,12 +16,16 @@
 
 package com.android.mediatranscodingtest;
 
+import static org.testng.Assert.assertThrows;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.media.MediaFormat;
 import android.media.MediaTranscodeManager;
 import android.media.MediaTranscodeManager.TranscodingJob;
 import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver;
+import android.media.TranscodingTestConfig;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.FileUtils;
@@ -69,6 +73,13 @@
     private static final String TAG = "MediaTranscodeManagerTest";
     /** The time to wait for the transcode operation to complete before failing the test. */
     private static final int TRANSCODE_TIMEOUT_SECONDS = 10;
+
+    /** Maximum number of retry to connect to the service. */
+    private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
+
+    /** Interval between trying to reconnect to the service. */
+    private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40;
+
     private Context mContext;
     private MediaTranscodeManager mMediaTranscodeManager = null;
     private Uri mSourceHEVCVideoUri = null;
@@ -129,13 +140,31 @@
         return format;
     }
 
+    private MediaTranscodeManager getManager() {
+        for (int count = 1;  count <= CONNECT_SERVICE_RETRY_COUNT; count++) {
+            Log.d(TAG, "Trying to connect to service. Try count: " + count);
+            MediaTranscodeManager manager = mContext.getSystemService(MediaTranscodeManager.class);
+            if (manager != null) {
+                return manager;
+            }
+            try {
+                // Sleep a bit before retry.
+                Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+
+        throw new UnsupportedOperationException("Failed to acquire MediaTranscodeManager");
+    }
+
     @Override
     public void setUp() throws Exception {
         Log.d(TAG, "setUp");
         super.setUp();
 
         mContext = getInstrumentation().getContext();
-        mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext, true /*retry*/);
+        mMediaTranscodeManager = getManager();
         assertNotNull(mMediaTranscodeManager);
         androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
 
@@ -155,6 +184,227 @@
         super.tearDown();
     }
 
+
+    /**
+     * Verify that setting null destination uri will throw exception.
+     */
+    @Test
+    public void testCreateTranscodingRequestWithNullDestinationUri() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> {
+            TranscodingRequest request =
+                    new TranscodingRequest.Builder()
+                            .setSourceUri(mSourceHEVCVideoUri)
+                            .setDestinationUri(null)
+                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                            .setVideoTrackFormat(createMediaFormat())
+                            .build();
+        });
+    }
+
+    /**
+     * Verify that setting null source uri will throw exception.
+     */
+    @Test
+    public void testCreateTranscodingRequestWithNullSourceUri() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> {
+            TranscodingRequest request =
+                    new TranscodingRequest.Builder()
+                            .setSourceUri(null)
+                            .setDestinationUri(mDestinationUri)
+                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                            .build();
+        });
+    }
+
+    /**
+     * Verify that not setting source uri will throw exception.
+     */
+    @Test
+    public void testCreateTranscodingRequestWithoutSourceUri() throws Exception {
+        assertThrows(UnsupportedOperationException.class, () -> {
+            TranscodingRequest request =
+                    new TranscodingRequest.Builder()
+                            .setDestinationUri(mDestinationUri)
+                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                            .setVideoTrackFormat(createMediaFormat())
+                            .build();
+        });
+    }
+
+    /**
+     * Verify that not setting destination uri will throw exception.
+     */
+    @Test
+    public void testCreateTranscodingRequestWithoutDestinationUri() throws Exception {
+        assertThrows(UnsupportedOperationException.class, () -> {
+            TranscodingRequest request =
+                    new TranscodingRequest.Builder()
+                            .setSourceUri(mSourceHEVCVideoUri)
+                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                            .setVideoTrackFormat(createMediaFormat())
+                            .build();
+        });
+    }
+
+    /**
+     * Verify that setting image transcoding mode will throw exception.
+     */
+    @Test
+    public void testCreateTranscodingRequestWithUnsupportedMode() throws Exception {
+        assertThrows(UnsupportedOperationException.class, () -> {
+            TranscodingRequest request =
+                    new TranscodingRequest.Builder()
+                            .setSourceUri(mSourceHEVCVideoUri)
+                            .setDestinationUri(mDestinationUri)
+                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_IMAGE)
+                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                            .setVideoTrackFormat(createMediaFormat())
+                            .build();
+        });
+    }
+
+    /**
+     * Verify that setting video transcoding without setting video format will throw exception.
+     */
+    @Test
+    public void testCreateTranscodingRequestWithoutVideoFormat() throws Exception {
+        assertThrows(UnsupportedOperationException.class, () -> {
+            TranscodingRequest request =
+                    new TranscodingRequest.Builder()
+                            .setSourceUri(mSourceHEVCVideoUri)
+                            .setDestinationUri(mDestinationUri)
+                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                            .build();
+        });
+    }
+
+    void testTranscodingWithExpectResult(Uri srcUri, Uri dstUri, int expectedResult)
+            throws Exception {
+        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+        TranscodingTestConfig testConfig = new TranscodingTestConfig();
+        testConfig.passThroughMode = true;
+        testConfig.processingTotalTimeMs = 300; // minimum time spent on transcoding.
+
+        TranscodingRequest request =
+                new TranscodingRequest.Builder()
+                        .setSourceUri(srcUri)
+                        .setDestinationUri(dstUri)
+                        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                        .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                        .setVideoTrackFormat(createMediaFormat())
+                        .setTestConfig(testConfig)
+                        .build();
+        Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+        TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+                transcodingJob -> {
+                    Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+                    assertTrue("Transcoding should failed.",
+                            transcodingJob.getResult() == expectedResult);
+                    transcodeCompleteSemaphore.release();
+                });
+        assertNotNull(job);
+
+        if (job != null) {
+            Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
+            boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+                    TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+            assertTrue("Transcode failed to complete in time.", finishedOnTime);
+        }
+
+        if (expectedResult == TranscodingJob.RESULT_SUCCESS) {
+            // Checks the destination file get generated.
+            File file = new File(dstUri.getPath());
+            assertTrue("Failed to create destination file", file.exists());
+
+            // Removes the file.
+            file.delete();
+        }
+    }
+
+    // Tests transcoding from invalid a invalid  and expects failure.
+    @Test
+    public void testTranscodingInvalidSrcUri() throws Exception {
+        Log.d(TAG, "Starting: testMediaTranscodeManager");
+
+        Uri invalidSrcUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
+                + mContext.getPackageName() + "/source.mp4");
+        Log.d(TAG, "Transcoding from source: " + invalidSrcUri);
+
+        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        // The full path of this file is:
+        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
+                + mContext.getPackageName() + "/temp.mp4");
+        Log.d(TAG, "Transcoding to destination: " + destinationUri);
+
+        testTranscodingWithExpectResult(invalidSrcUri, destinationUri, TranscodingJob.RESULT_ERROR);
+    }
+
+    // Tests transcoding to a uri in res folder and expects failure as we could not write to res
+    // folder.
+    @Test
+    public void testTranscodingToResFolder() throws Exception {
+        Log.d(TAG, "Starting: testMediaTranscodeManager");
+
+        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        // The full path of this file is:
+        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
+                + mContext.getPackageName() + "/temp.mp4");
+        Log.d(TAG, "Transcoding to destination: " + destinationUri);
+
+        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
+                TranscodingJob.RESULT_ERROR);
+    }
+
+    // Tests transcoding to a uri in internal storage folder and expects success.
+    @Test
+    public void testTranscodingToCacheDir() throws Exception {
+        Log.d(TAG, "Starting: testMediaTranscodeManager");
+
+        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        // The full path of this file is:
+        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
+        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+                + mContext.getCacheDir().getAbsolutePath() + "/temp.mp4");
+
+        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
+                TranscodingJob.RESULT_SUCCESS);
+    }
+
+    // Tests transcoding to a uri in internal files directory and expects success.
+    @Test
+    public void testTranscodingToInternalFilesDir() throws Exception {
+        Log.d(TAG, "Starting: testMediaTranscodeManager");
+
+        // Create a file Uri:
+        // file:///storage/emulated/0/Android/data/com.android.mediatranscodingtest/files/temp.mp4
+        Uri destinationUri = Uri.fromFile(new File(mContext.getFilesDir(), "temp.mp4"));
+        Log.i(TAG, "Transcoding to files dir: " + destinationUri);
+
+        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
+                TranscodingJob.RESULT_SUCCESS);
+    }
+
+    // Tests transcoding to a uri in external files directory and expects success.
+    @Test
+    public void testTranscodingToExternalFilesDir() throws Exception {
+        Log.d(TAG, "Starting: testMediaTranscodeManager");
+
+        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/files/temp.mp4
+        Uri destinationUri = Uri.fromFile(new File(mContext.getExternalFilesDir(null), "temp.mp4"));
+        Log.i(TAG, "Transcoding to files dir: " + destinationUri);
+
+        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
+                TranscodingJob.RESULT_SUCCESS);
+    }
+
     @Test
     public void testTranscodingFromHevcToAvc() throws Exception {
         Semaphore transcodeCompleteSemaphore = new Semaphore(0);
@@ -165,17 +415,27 @@
         Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
                 + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
 
+        Bundle clientCaps = new Bundle();
+        clientCaps.putBoolean(MediaFormatResolver.CAPS_SUPPORTS_HEVC, false);
+        MediaFormatResolver resolver = new MediaFormatResolver()
+                .setSourceVideoFormatHint(MediaFormat.createVideoFormat(
+                        MediaFormat.MIMETYPE_VIDEO_HEVC, WIDTH, HEIGHT))
+                .setClientCapabilities(clientCaps);
+        assertTrue(resolver.shouldTranscode());
+        MediaFormat videoTrackFormat = resolver.resolveVideoFormat();
+        assertNotNull(videoTrackFormat);
+
         TranscodingRequest request =
                 new TranscodingRequest.Builder()
                         .setSourceUri(mSourceHEVCVideoUri)
                         .setDestinationUri(destinationUri)
                         .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
                         .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                        .setVideoTrackFormat(createMediaFormat())
+                        .setVideoTrackFormat(videoTrackFormat)
                         .build();
         Executor listenerExecutor = Executors.newSingleThreadExecutor();
 
-        Log.i(TAG, "transcoding to " + createMediaFormat());
+        Log.i(TAG, "transcoding to " + videoTrackFormat);
 
         TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
                 transcodingJob -> {
@@ -283,7 +543,7 @@
                     int mPreviousProgress = 0;
 
                     @Override
-                    public void onProgressUpdate(int newProgress) {
+                    public void onProgressUpdate(TranscodingJob job, int newProgress) {
                         assertTrue("Invalid proress update", newProgress > mPreviousProgress);
                         assertTrue("Invalid proress update", newProgress <= 100);
                         if (newProgress > 0) {
@@ -329,73 +589,5 @@
         return UiDevice.getInstance(
                 InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
     }
-
-    @Test
-    public void testHandleTranscoderServiceDied() throws Exception {
-        try {
-            if (!checkIfRoot()) {
-                throw new AssertionError("must be root to run this test; try adb root?");
-            } else {
-                Log.i(TAG, "Device is root");
-            }
-        } catch (IOException e) {
-            throw new AssertionError(e);
-        }
-
-        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
-        Semaphore jobStartedSemaphore = new Semaphore(0);
-
-        // Transcode a 15 seconds video, so that the transcoding is not finished when we kill the
-        // service.
-        Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
-                + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4");
-        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
-                + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
-
-        TranscodingRequest request =
-                new TranscodingRequest.Builder()
-                        .setSourceUri(mSourceHEVCVideoUri)
-                        .setDestinationUri(destinationUri)
-                        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-                        .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                        .setVideoTrackFormat(createMediaFormat())
-                        .build();
-        Executor listenerExecutor = Executors.newSingleThreadExecutor();
-
-        Log.i(TAG, "transcoding to " + createMediaFormat());
-
-        TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
-                transcodingJob -> {
-                    Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
-                    assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_ERROR);
-                    transcodeCompleteSemaphore.release();
-                });
-        assertNotNull(job);
-
-        AtomicInteger progressUpdateCount = new AtomicInteger(0);
-
-        // Set progress update executor and use the same executor as result listener.
-        job.setOnProgressUpdateListener(listenerExecutor,
-                new TranscodingJob.OnProgressUpdateListener() {
-                    @Override
-                    public void onProgressUpdate(int newProgress) {
-                        if (newProgress > 0) {
-                            jobStartedSemaphore.release();
-                        }
-                    }
-                });
-
-        // Wait for progress update so the job is in running state.
-        jobStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        assertTrue("Job is not running", job.getStatus() == TranscodingJob.STATUS_RUNNING);
-
-        // Kills the service and expects receiving failure of the job.
-        executeShellCommand("pkill -f media.transcoding");
-
-        Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode result.");
-        boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
-                TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
-    }
 }
 
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java
deleted file mode 100644
index 748c21a..0000000
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerWithMockServiceTest.java
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Copyright (C) 2019 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.mediatranscodingtest;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.media.IMediaTranscodingService;
-import android.media.ITranscodingClient;
-import android.media.ITranscodingClientCallback;
-import android.media.MediaFormat;
-import android.media.MediaTranscodeManager;
-import android.media.MediaTranscodeManager.TranscodingJob;
-import android.media.MediaTranscodeManager.TranscodingRequest;
-import android.media.TranscodingJobParcel;
-import android.media.TranscodingRequestParcel;
-import android.media.TranscodingResultParcel;
-import android.media.TranscodingTestConfig;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-
-import org.junit.Test;
-
-import java.io.File;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/*
- * Functional tests for MediaTranscodeManager in the media framework.
- * The test uses a mock Transcoding service as backend to test the API functionality.
- *
- * To run this test suite:
-     make frameworks/base/media/tests/MediaTranscodingTest
-     make mediatranscodingtest
-
-     adb install -r testcases/mediatranscodingtest/arm64/mediatranscodingtest.apk
-
-     adb shell am instrument -e class \
-     com.android.mediatranscodingtest.MediaTranscodeManagerWithMockServiceTest \
-     -w com.android.mediatranscodingtest/.MediaTranscodingTestRunner
- *
- */
-public class MediaTranscodeManagerWithMockServiceTest
-        extends ActivityInstrumentationTestCase2<MediaTranscodingTest> {
-    private static final String TAG = "MediaTranscodeManagerWithMockServiceTest";
-    /** The time to wait for the transcode operation to complete before failing the test. */
-    private static final int TRANSCODE_TIMEOUT_SECONDS = 2;
-    private Context mContext;
-    private MediaTranscodeManager mMediaTranscodeManager = null;
-    private Uri mSourceHEVCVideoUri = null;
-    private Uri mDestinationUri = null;
-    // Use mock transcoding service for testing the api.
-    private MockTranscodingService mTranscodingService = null;
-
-    // Setting for transcoding to H.264.
-    private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
-    private static final int BIT_RATE = 2000000;            // 2Mbps
-    private static final int WIDTH = 1920;
-    private static final int HEIGHT = 1080;
-
-    // A mock transcoding service that will take constant 300ms to process each transcoding job.
-    // Instead of doing real transcoding, it will return the dst uri directly.
-    class MockTranscodingService extends IMediaTranscodingService.Stub {
-        private final ScheduledExecutorService mJobScheduler = Executors.newScheduledThreadPool(1);
-        private int mNumOfClients = 0;
-        private AtomicInteger mJobId = new AtomicInteger();
-
-        // A runnable that will process the job.
-        private class ProcessingJobRunnable implements Runnable {
-            private TranscodingJobParcel mJob;
-            private ITranscodingClientCallback mCallback;
-            private ConcurrentMap<Integer, ScheduledFuture<?>> mJobMap;
-
-            ProcessingJobRunnable(ITranscodingClientCallback callback,
-                    TranscodingJobParcel job,
-                    ConcurrentMap<Integer, ScheduledFuture<?>> jobMap) {
-                mJob = job;
-                mCallback = callback;
-                mJobMap = jobMap;
-            }
-
-            @Override
-            public void run() {
-                Log.d(TAG, "Start to process job " + mJob.jobId);
-                TranscodingResultParcel result = new TranscodingResultParcel();
-                try {
-                    // Try to open the source fd.
-                    ParcelFileDescriptor sourceFd = mCallback.openFileDescriptor(
-                            mJob.request.sourceFilePath, "r");
-                    if (sourceFd == null) {
-                        Log.d(TAG, "Failed to open sourceFd");
-                        // TODO(hkuang): Pass the correct error code.
-                        mCallback.onTranscodingFailed(mJob.jobId, 1);
-                        return;
-                    } else {
-                        Log.d(TAG, "Successfully open sourceFd");
-                    }
-
-                    // Try to write to the destination fd.
-                    ParcelFileDescriptor destinationFd = mCallback.openFileDescriptor(
-                            mJob.request.destinationFilePath, "w");
-                    if (destinationFd == null) {
-                        Log.d(TAG, "Failed to open destinationFd");
-                        // TODO(hkuang): Pass the correct error code.
-                        mCallback.onTranscodingFailed(mJob.jobId, 1);
-                        return;
-                    } else {
-                        Log.d(TAG, "Successfully open destinationFd");
-                    }
-
-                    mCallback.onTranscodingFinished(mJob.jobId, result);
-                    // Removes the job from job map.
-                    mJobMap.remove(mJob.jobId);
-                } catch (RemoteException re) {
-                    Log.e(TAG, "Failed to callback to client");
-                }
-            }
-        }
-
-        @Override
-        public ITranscodingClient registerClient(ITranscodingClientCallback callback,
-                String clientName, String opPackageName, int clientUid, int clientPid)
-                throws RemoteException {
-            Log.d(TAG, "MockTranscodingService creates one client");
-
-            ITranscodingClient client = new ITranscodingClient.Stub() {
-                private final ConcurrentMap<Integer, ScheduledFuture<?>> mPendingTranscodingJobs =
-                        new ConcurrentHashMap<Integer, ScheduledFuture<?>>();
-
-                @Override
-                public boolean submitRequest(TranscodingRequestParcel inRequest,
-                        TranscodingJobParcel outjob) {
-                    Log.d(TAG, "Mock client gets submitRequest");
-                    try {
-                        outjob.request = inRequest;
-                        outjob.jobId = mJobId.getAndIncrement();
-                        Log.i(TAG, "Generate new job " + outjob.jobId);
-                        Log.i(TAG, "Source Uri " + inRequest.sourceFilePath);
-                        Log.i(TAG, "Destination Uri " + inRequest.destinationFilePath);
-
-                        // Schedules the job to run after inRequest.processingDelayMs.
-                        ScheduledFuture<?> transcodingFuture = mJobScheduler.schedule(
-                                new ProcessingJobRunnable(callback, outjob,
-                                        mPendingTranscodingJobs),
-                                inRequest.testConfig == null ? 0
-                                        : inRequest.testConfig.processingTotalTimeMs,
-                                TimeUnit.MILLISECONDS);
-                        mPendingTranscodingJobs.put(outjob.jobId, transcodingFuture);
-                    } catch (RejectedExecutionException e) {
-                        Log.e(TAG, "Failed to schedule transcoding job: " + e);
-                        return false;
-                    }
-
-                    return true;
-                }
-
-                @Override
-                public boolean cancelJob(int jobId) throws RemoteException {
-                    Log.d(TAG, "Mock client gets cancelJob " + jobId);
-                    // Cancels the job is still in the mPendingTranscodingJobs.
-                    if (mPendingTranscodingJobs.containsKey(jobId)) {
-                        // Cancel the future task for transcoding.
-                        mPendingTranscodingJobs.get(jobId).cancel(true);
-
-                        // Remove the job from the mPendingTranscodingJobs.
-                        mPendingTranscodingJobs.remove(jobId);
-                        return true;
-                    }
-                    return false;
-                }
-
-                @Override
-                public boolean getJobWithId(int jobId, TranscodingJobParcel job)
-                        throws RemoteException {
-                    // This will be implemented this if needed in the test.
-                    return true;
-                }
-
-                @Override
-                public void unregister() throws RemoteException {
-                    Log.d(TAG, "Mock client gets unregister");
-                    // This will be implemented this if needed in the test.
-                    mNumOfClients--;
-                }
-            };
-            mNumOfClients++;
-            return client;
-        }
-
-        @Override
-        public int getNumOfClients() throws RemoteException {
-            return mNumOfClients;
-        }
-    }
-
-    public MediaTranscodeManagerWithMockServiceTest() {
-        super("com.android.MediaTranscodeManagerWithMockServiceTest", MediaTranscodingTest.class);
-    }
-
-    private static Uri resourceToUri(Context context, int resId) {
-        Uri uri = new Uri.Builder()
-                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
-                .authority(context.getResources().getResourcePackageName(resId))
-                .appendPath(context.getResources().getResourceTypeName(resId))
-                .appendPath(context.getResources().getResourceEntryName(resId))
-                .build();
-        return uri;
-    }
-
-    private static Uri generateNewUri(Context context, String filename) {
-        File outFile = new File(context.getExternalCacheDir(), filename);
-        return Uri.fromFile(outFile);
-    }
-
-    // Generates a invalid uri which will let the mock service return transcoding failure.
-    private static Uri generateInvalidTranscodingUri(Context context) {
-        File outFile = new File(context.getExternalCacheDir(), "InvalidUri.mp4");
-        return Uri.fromFile(outFile);
-    }
-
-    /**
-     * Creates a MediaFormat with the basic set of values.
-     */
-    private static MediaFormat createMediaFormat() {
-        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);
-        format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
-        return format;
-    }
-
-    @Override
-    public void setUp() throws Exception {
-        Log.d(TAG, "setUp");
-        super.setUp();
-        mTranscodingService = new MockTranscodingService();
-        mContext = getInstrumentation().getContext();
-        mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext, mTranscodingService);
-        assertNotNull(mMediaTranscodeManager);
-
-        // Setup source HEVC file uri.
-        mSourceHEVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyHEVC);
-
-        // Setup destination file.
-        mDestinationUri = generateNewUri(mContext, "transcoded.mp4");
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-    /**
-     * Verify that setting null destination uri will throw exception.
-     */
-    @Test
-    public void testCreateTranscodingRequestWithNullDestinationUri() throws Exception {
-        assertThrows(IllegalArgumentException.class, () -> {
-            TranscodingRequest request =
-                    new TranscodingRequest.Builder()
-                            .setSourceUri(mSourceHEVCVideoUri)
-                            .setDestinationUri(null)
-                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                            .setVideoTrackFormat(createMediaFormat())
-                            .build();
-        });
-    }
-
-    /**
-     * Verify that setting null source uri will throw exception.
-     */
-    @Test
-    public void testCreateTranscodingRequestWithNullSourceUri() throws Exception {
-        assertThrows(IllegalArgumentException.class, () -> {
-            TranscodingRequest request =
-                    new TranscodingRequest.Builder()
-                            .setSourceUri(null)
-                            .setDestinationUri(mDestinationUri)
-                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-                            .build();
-        });
-    }
-
-    /**
-     * Verify that not setting source uri will throw exception.
-     */
-    @Test
-    public void testCreateTranscodingRequestWithoutSourceUri() throws Exception {
-        assertThrows(UnsupportedOperationException.class, () -> {
-            TranscodingRequest request =
-                    new TranscodingRequest.Builder()
-                            .setDestinationUri(mDestinationUri)
-                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                            .setVideoTrackFormat(createMediaFormat())
-                            .build();
-        });
-    }
-
-    /**
-     * Verify that not setting destination uri will throw exception.
-     */
-    @Test
-    public void testCreateTranscodingRequestWithoutDestinationUri() throws Exception {
-        assertThrows(UnsupportedOperationException.class, () -> {
-            TranscodingRequest request =
-                    new TranscodingRequest.Builder()
-                            .setSourceUri(mSourceHEVCVideoUri)
-                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                            .setVideoTrackFormat(createMediaFormat())
-                            .build();
-        });
-    }
-
-    /**
-     * Verify that setting image transcoding mode will throw exception.
-     */
-    @Test
-    public void testCreateTranscodingRequestWithUnsupportedMode() throws Exception {
-        assertThrows(UnsupportedOperationException.class, () -> {
-            TranscodingRequest request =
-                    new TranscodingRequest.Builder()
-                            .setSourceUri(mSourceHEVCVideoUri)
-                            .setDestinationUri(mDestinationUri)
-                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_IMAGE)
-                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                            .setVideoTrackFormat(createMediaFormat())
-                            .build();
-        });
-    }
-
-    /**
-     * Verify that setting video transcoding without setting video format will throw exception.
-     */
-    @Test
-    public void testCreateTranscodingRequestWithoutVideoFormat() throws Exception {
-        assertThrows(UnsupportedOperationException.class, () -> {
-            TranscodingRequest request =
-                    new TranscodingRequest.Builder()
-                            .setSourceUri(mSourceHEVCVideoUri)
-                            .setDestinationUri(mDestinationUri)
-                            .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-                            .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                            .build();
-        });
-    }
-
-    void testTranscodingWithExpectResult(Uri srcUri, Uri dstUri, int expectedResult)
-            throws Exception {
-        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
-        TranscodingTestConfig testConfig = new TranscodingTestConfig();
-        testConfig.passThroughMode = true;
-        testConfig.processingTotalTimeMs = 300; // minimum time spent on transcoding.
-
-        TranscodingRequest request =
-                new TranscodingRequest.Builder()
-                        .setSourceUri(srcUri)
-                        .setDestinationUri(dstUri)
-                        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
-                        .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
-                        .setVideoTrackFormat(createMediaFormat())
-                        .setTestConfig(testConfig)
-                        .build();
-        Executor listenerExecutor = Executors.newSingleThreadExecutor();
-
-        TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
-                transcodingJob -> {
-                    Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
-                    assertTrue("Transcoding should failed.",
-                            transcodingJob.getResult() == expectedResult);
-                    transcodeCompleteSemaphore.release();
-                });
-        assertNotNull(job);
-
-        if (job != null) {
-            Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode to complete.");
-            boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
-                    TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-            assertTrue("Transcode failed to complete in time.", finishedOnTime);
-        }
-
-        if (expectedResult == TranscodingJob.RESULT_SUCCESS) {
-            // Checks the destination file get generated.
-            File file = new File(dstUri.getPath());
-            assertTrue("Failed to create destination file", file.exists());
-
-            // Removes the file.
-            file.delete();
-        }
-    }
-
-    // Tests transcoding from invalid a invalid  and expects failure.
-    @Test
-    public void testTranscodingInvalidSrcUri() throws Exception {
-        Log.d(TAG, "Starting: testMediaTranscodeManager");
-
-        Uri invalidSrcUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
-                + mContext.getPackageName() + "/source.mp4");
-        Log.d(TAG, "Transcoding from source: " + invalidSrcUri);
-
-        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
-        // The full path of this file is:
-        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
-        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
-                + mContext.getPackageName() + "/temp.mp4");
-        Log.d(TAG, "Transcoding to destination: " + destinationUri);
-
-        testTranscodingWithExpectResult(invalidSrcUri, destinationUri, TranscodingJob.RESULT_ERROR);
-    }
-
-    // Tests transcoding to a uri in res folder and expects failure as we could not write to res
-    // folder.
-    @Test
-    public void testTranscodingToResFolder() throws Exception {
-        Log.d(TAG, "Starting: testMediaTranscodeManager");
-
-        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
-        // The full path of this file is:
-        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
-        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
-                + mContext.getPackageName() + "/temp.mp4");
-        Log.d(TAG, "Transcoding to destination: " + destinationUri);
-
-        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
-                TranscodingJob.RESULT_ERROR);
-    }
-
-    // Tests transcoding to a uri in internal storage folder and expects success.
-    @Test
-    public void testTranscodingToCacheDir() throws Exception {
-        Log.d(TAG, "Starting: testMediaTranscodeManager");
-
-        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
-        // The full path of this file is:
-        // /data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
-        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
-                + mContext.getCacheDir().getAbsolutePath() + "/temp.mp4");
-
-        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
-                TranscodingJob.RESULT_SUCCESS);
-    }
-
-    // Tests transcoding to a uri in internal files directory and expects success.
-    @Test
-    public void testTranscodingToInternalFilesDir() throws Exception {
-        Log.d(TAG, "Starting: testMediaTranscodeManager");
-
-        // Create a file Uri:
-        // file:///storage/emulated/0/Android/data/com.android.mediatranscodingtest/files/temp.mp4
-        Uri destinationUri = Uri.fromFile(new File(mContext.getFilesDir(), "temp.mp4"));
-        Log.i(TAG, "Transcoding to files dir: " + destinationUri);
-
-        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
-                TranscodingJob.RESULT_SUCCESS);
-    }
-
-    // Tests transcoding to a uri in external files directory and expects success.
-    @Test
-    public void testTranscodingToExternalFilesDir() throws Exception {
-        Log.d(TAG, "Starting: testMediaTranscodeManager");
-
-        // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/files/temp.mp4
-        Uri destinationUri = Uri.fromFile(new File(mContext.getExternalFilesDir(null), "temp.mp4"));
-        Log.i(TAG, "Transcoding to files dir: " + destinationUri);
-
-        testTranscodingWithExpectResult(mSourceHEVCVideoUri, destinationUri,
-                TranscodingJob.RESULT_SUCCESS);
-    }
-
-    @Test
-    public void testTranscodingOneVideo() throws Exception {
-        Log.d(TAG, "Starting: testMediaTranscodeManager");
-        testTranscodingWithExpectResult(mSourceHEVCVideoUri, mDestinationUri,
-                TranscodingJob.RESULT_SUCCESS);
-    }
-}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
index 5c87d30..73e1d98 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -89,7 +89,7 @@
         Log.d(TAG, "setUp");
         super.setUp();
         mContext = getInstrumentation().getContext();
-        mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
+        mMediaTranscodeManager = mContext.getSystemService(MediaTranscodeManager.class);
     }
 
     @Override
@@ -132,9 +132,9 @@
                         Log.d(TAG,
                                 "Transcoding completed with result: " + transcodingJob.getResult());
                         assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_SUCCESS);
-                        transcodeCompleteSemaphore.release();
                         transcodingTime.set(System.currentTimeMillis() - startTimeMs);
                         totalTimeMs.addAndGet(transcodingTime.get());
+                        transcodeCompleteSemaphore.release();
                     });
 
             if (job != null) {
@@ -173,9 +173,6 @@
         transcode(testVideoName, transcodedVideoName);
     }
 
-    // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
-    //  more long time that leads to timeout failure.
-    /*
     @Test
     public void testBenchmarkingAVCToAVCWith66FramesWithAudio() throws Exception {
         String videoNameWithoutExtension = "video_1920x1080_66frame_h264_22Mbps_30fps_aac";
@@ -183,7 +180,7 @@
         String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
 
         transcode(testVideoName, transcodedVideoName);
-    }*/
+    }
 
     @Test
     public void testBenchmarkingAVCToAVCWith361FramesWithoutAudio() throws Exception {
@@ -194,16 +191,14 @@
         transcode(testVideoName, transcodedVideoName);
     }
 
-    // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
-    //  more long time that leads to timeout failure.
-    /*@Test
+    @Test
     public void testBenchmarkingAVCToAVCWith361FramesWithAudio() throws Exception {
         String videoNameWithoutExtension = "video_1920x1080_361frame_h264_22Mbps_30fps_aac";
         String testVideoName = videoNameWithoutExtension + ".mp4";
         String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
 
         transcode(testVideoName, transcodedVideoName);
-    }*/
+    }
 
     @Test
     public void testBenchmarkingAVCToAVCWith943FramesWithoutAudio() throws Exception {
@@ -214,16 +209,14 @@
         transcode(testVideoName, transcodedVideoName);
     }
 
-    // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
-    //  more long time that leads to timeout failure.
-   /* @Test
+    @Test
     public void testBenchmarkingAVCToAVCWith943FramesWithAudio() throws Exception {
         String videoNameWithoutExtension = "video_1920x1080_943frame_h264_22Mbps_30fps_aac";
         String testVideoName = videoNameWithoutExtension + ".mp4";
         String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
 
         transcode(testVideoName, transcodedVideoName);
-    }*/
+    }
 
     @Test
     public void testBenchmarkingAVCToAVCWith1822FramesWithoutAudio() throws Exception {
@@ -234,16 +227,14 @@
         transcode(testVideoName, transcodedVideoName);
     }
 
-    // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
-    //  more long time that leads to timeout failure.
-    /*@Test
+    @Test
     public void testBenchmarkingAVCToAVCWith1822FramesWithAudio() throws Exception {
         String videoNameWithoutExtension = "video_1920x1080_1822frame_h264_22Mbps_30fps_aac";
         String testVideoName = videoNameWithoutExtension + ".mp4";
         String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
 
         transcode(testVideoName, transcodedVideoName);
-    }*/
+    }
 
     @Test
     public void testBenchmarkingAVCToAVCWith3648FramesWithoutAudio() throws Exception {
@@ -254,16 +245,14 @@
         transcode(testVideoName, transcodedVideoName);
     }
 
-    // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
-    //  more long time that leads to timeout failure.
-    /*@Test
+    @Test
     public void testBenchmarkingAVCToAVCWith3648FramesWithAudio() throws Exception {
         String videoNameWithoutExtension = "video_1920x1080_3648frame_h264_22Mbps_30fps_aac";
         String testVideoName = videoNameWithoutExtension + ".mp4";
         String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
 
         transcode(testVideoName, transcodedVideoName);
-    }*/
+    }
 
     @Test
     public void testBenchmarkingAVCToAVCWith11042FramesWithoutAudio() throws Exception {
@@ -274,16 +263,14 @@
         transcode(testVideoName, transcodedVideoName);
     }
 
-    // TODO(hkuang): Enable this after b/160268606 is fixed. Transcoding video with audio takes way
-    //  more long time that leads to timeout failure.
-    /*@Test
+    @Test
     public void testBenchmarkingAVCToAVCWith11042FramesWithAudio() throws Exception {
         String videoNameWithoutExtension = "video_1920x1080_11042frame_h264_22Mbps_30fps_aac";
         String testVideoName = videoNameWithoutExtension + ".mp4";
         String transcodedVideoName = videoNameWithoutExtension + "_transcode.mp4";
 
         transcode(testVideoName, transcodedVideoName);
-    } */
+    }
 
     @Test
     public void testBenchmarkingHEVCToAVCWith107FramesWithoutAudio() throws Exception {
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
index 53b2392..bd1551f 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingTestRunner.java
@@ -36,8 +36,8 @@
     @Override
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(MediaTranscodeManagerDiedTest.class);
         suite.addTestSuite(MediaTranscodeManagerTest.class);
-        suite.addTestSuite(MediaTranscodeManagerWithMockServiceTest.class);
         suite.addTestSuite(MediaTranscodingBenchmark.class);
         return suite;
     }
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index e0ebec6..bb40e24 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -1005,7 +1005,7 @@
     field public static final int numericModifiers = 16844111; // 0x101054f
     field public static final int numericShortcut = 16843236; // 0x10101e4
     field public static final int offset = 16844052; // 0x1010514
-    field public static final int onClick = 16843375; // 0x101026f
+    field @Deprecated public static final int onClick = 16843375; // 0x101026f
     field public static final int oneshot = 16843159; // 0x1010197
     field public static final int opacity = 16843550; // 0x101031e
     field public static final int opticalInsetBottom = 16844171; // 0x101058b
@@ -6188,6 +6188,7 @@
     method public android.app.PictureInPictureParams build();
     method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
     method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
+    method @NonNull public android.app.PictureInPictureParams.Builder setAutoEnterAllowed(boolean);
     method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
   }
 
@@ -11617,6 +11618,16 @@
     field public int version;
   }
 
+  public final class FileChecksum implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getKind();
+    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getSplitName();
+    method @NonNull public byte[] getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
+  }
+
   public final class InstallSourceInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getInitiatingPackageName();
@@ -11992,6 +12003,7 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
+    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12082,6 +12094,7 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
+    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12236,6 +12249,8 @@
     field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
     field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12245,9 +12260,16 @@
     field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int SYNCHRONOUS = 2; // 0x2
+    field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
+    field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
+    field public static final int WHOLE_MD5 = 2; // 0x2
+    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field public static final int WHOLE_SHA1 = 4; // 0x4
+    field public static final int WHOLE_SHA256 = 8; // 0x8
+    field public static final int WHOLE_SHA512 = 16; // 0x10
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -46525,7 +46547,8 @@
     method public boolean isVoiceCapable();
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
-    method public void listen(android.telephony.PhoneStateListener, int);
+    method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
+    method public void listen(long, @NonNull android.telephony.PhoneStateListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(String);
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 8892a29..1bbbf98 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -35,13 +35,24 @@
 package android.media {
 
   public class AudioManager {
+    method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+    method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
+    method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public static final class MediaMetadata.Builder {
+    ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+  }
+
 }
 
 package android.media.session {
 
+  public static final class MediaController.PlaybackInfo implements android.os.Parcelable {
+    ctor public MediaController.PlaybackInfo(int, int, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.media.AudioAttributes, @Nullable String);
+  }
+
   public final class MediaSession {
     field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000
   }
@@ -84,6 +95,14 @@
 
 }
 
+package android.provider {
+
+  public final class DeviceConfig {
+    field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
+  }
+
+}
+
 package android.util {
 
   public class AtomicFile {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index d1264df..ff3999f 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -219,6 +219,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
@@ -1680,6 +1681,7 @@
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
+    field public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
     field public static final String NETD_SERVICE = "netd";
     field public static final String NETWORK_SCORE_SERVICE = "network_score";
     field public static final String OEM_LOCK_SERVICE = "oem_lock";
@@ -4137,8 +4139,10 @@
   public class AudioManager {
     method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
     method public void clearAudioServerStateCallback();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
@@ -4149,6 +4153,7 @@
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
@@ -4157,6 +4162,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removePreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
@@ -4166,6 +4172,7 @@
     method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
@@ -4195,6 +4202,10 @@
     method @Deprecated public void onPreferredDeviceForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @Nullable android.media.AudioDeviceAttributes);
   }
 
+  public static interface AudioManager.OnPreferredDevicesForCapturePresetChangedListener {
+    method public void onPreferredDevicesForCapturePresetChanged(int, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
+  }
+
   public static interface AudioManager.OnPreferredDevicesForStrategyChangedListener {
     method public void onPreferredDevicesForStrategyChanged(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
   }
@@ -4272,6 +4283,65 @@
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
   }
 
+  public final class MediaTranscodeManager {
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+    field public static final int PRIORITY_REALTIME = 1; // 0x1
+    field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
+  }
+
+  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
+    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob);
+  }
+
+  public static final class MediaTranscodeManager.TranscodingJob {
+    method public void cancel();
+    method public int getJobId();
+    method @IntRange(from=0, to=100) public int getProgress();
+    method public int getResult();
+    method public int getStatus();
+    method public boolean retry();
+    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+    method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
+    field public static final int RESULT_CANCELED = 4; // 0x4
+    field public static final int RESULT_ERROR = 3; // 0x3
+    field public static final int RESULT_NONE = 1; // 0x1
+    field public static final int RESULT_SUCCESS = 2; // 0x2
+    field public static final int STATUS_FINISHED = 3; // 0x3
+    field public static final int STATUS_PAUSED = 4; // 0x4
+    field public static final int STATUS_PENDING = 1; // 0x1
+    field public static final int STATUS_RUNNING = 2; // 0x2
+  }
+
+  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener {
+    method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingJob, @IntRange(from=0, to=100) int);
+  }
+
+  public static final class MediaTranscodeManager.TranscodingRequest {
+    method @NonNull public android.net.Uri getDestinationUri();
+    method public int getPriority();
+    method @NonNull public android.net.Uri getSourceUri();
+    method public int getType();
+    method @Nullable public android.media.MediaFormat getVideoTrackFormat();
+  }
+
+  public static final class MediaTranscodeManager.TranscodingRequest.Builder {
+    ctor public MediaTranscodeManager.TranscodingRequest.Builder();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int);
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat);
+  }
+
+  public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver {
+    ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver();
+    method @Nullable public android.media.MediaFormat resolveVideoFormat();
+    method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat);
+    method public boolean shouldTranscode();
+    field public static final String CAPS_SUPPORTS_HEVC = "support-hevc";
+  }
+
   public class PlayerProxy {
     method public void pause();
     method public void setPan(float);
@@ -6811,7 +6881,7 @@
     method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
     method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
-    method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
+    method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
     method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SendMgmtFrameCallback);
     method public void setOnServiceDeadCallback(@NonNull Runnable);
     method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
@@ -6862,10 +6932,10 @@
     field public final int txBitrateMbps;
   }
 
-  public static interface WifiNl80211Manager.SoftApCallback {
-    method public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
-    method public void onFailure();
-    method public void onSoftApChannelSwitched(int, int);
+  @Deprecated public static interface WifiNl80211Manager.SoftApCallback {
+    method @Deprecated public void onConnectedClientsChanged(@NonNull android.net.wifi.nl80211.NativeWifiClient, boolean);
+    method @Deprecated public void onFailure();
+    method @Deprecated public void onSoftApChannelSwitched(int, int);
   }
 
   public static class WifiNl80211Manager.TxPacketCounters {
@@ -9686,6 +9756,7 @@
     method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
     method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+    method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
@@ -9693,12 +9764,29 @@
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 4294967296L; // 0x100000000L
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
+  public final class PhysicalChannelConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCellBandwidthDownlink();
+    method public int getChannelNumber();
+    method public int getConnectionStatus();
+    method public int getNetworkType();
+    method @IntRange(from=0, to=1007) public int getPhysicalCellId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
+    field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+    field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+    field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
+    field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
+  }
+
   public final class PreciseCallState implements android.os.Parcelable {
     ctor public PreciseCallState(int, int, int, int, int);
     method public int describeContents();
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/res/drawable/car_ic_apps.xml
index a8d8a2f..e028a0e 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_apps.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps.xml
@@ -15,11 +15,11 @@
   ~ limitations under the License
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
         android:viewportWidth="44"
-        android:viewportHeight="44"
-        android:width="44dp"
-        android:height="44dp">
-<path
-    android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z"
-    android:fillColor="@color/car_nav_icon_fill_color" />
+        android:viewportHeight="44">
+    <path
+        android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
 </vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml
index 2a4e91a..9504e61 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_apps_selected.xml
@@ -15,10 +15,10 @@
   ~ limitations under the License
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
         android:viewportWidth="44"
-        android:viewportHeight="44"
-        android:width="44dp"
-        android:height="44dp">
+        android:viewportHeight="44">
     <path
         android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z"
         android:fillColor="@color/car_nav_icon_fill_color_selected" />
diff --git a/packages/CarSystemUI/res/drawable/car_ic_home.xml b/packages/CarSystemUI/res/drawable/car_ic_home.xml
new file mode 100644
index 0000000..c78f0ed
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_home.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_home_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_home_selected.xml
new file mode 100644
index 0000000..16192df
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_home_selected.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"
+        android:fillColor="@color/car_nav_icon_fill_color_selected" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml
index bdc44b3..55c968e 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_hvac.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
     Copyright (C) 2018 The Android Open Source Project
 
@@ -14,38 +15,11 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="37dp"
-    android:height="31dp"
-    android:viewportWidth="37"
-    android:viewportHeight="31">
-
-    <group
-            android:translateX="-4.000000"
-            android:translateY="-6.000000">
-        <group
-                android:translateX="5.000000"
-                android:translateY="5.000000">
-            <path
-                android:fillType="evenOdd"
-                android:strokeColor="#FAFAFA"
-                android:strokeWidth="3.5"
-                android:pathData="M0.320769938,6.07518051 C6.46754647,1.46509811 12.4222362,1.46509811
-18.1848392,6.07518051 C23.9474422,10.6852629 29.3258717,10.4931761
-34.3201276,5.49892021" />
-            <path
-                android:fillType="evenOdd"
-                android:strokeColor="#FAFAFA"
-                android:strokeWidth="3.5"
-                android:pathData="M0.320769938,17.0751805 C6.46754647,12.4650981 12.4222362,12.4650981
-18.1848392,17.0751805 C23.9474422,21.6852629 29.3258717,21.4931761
-34.3201276,16.4989202" />
-            <path
-                android:fillType="evenOdd"
-                android:strokeColor="#FAFAFA"
-                android:strokeWidth="3.5"
-                android:pathData="M0.320769938,28.0751805 C6.46754647,23.4650981 12.4222362,23.4650981
-18.1848392,28.0751805 C23.9474422,32.6852629 29.3258717,32.4931761
-34.3201276,27.4989202" />
-        </group>
-    </group>
-</vector>
\ No newline at end of file
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M16.34,8.36l-2.29,0.82c-0.18,-0.13 -0.38,-0.25 -0.58,-0.34c0.17,-0.83 0.63,-1.58 1.36,-2.06C16.85,5.44 16.18,2 13.39,2C9,2 7.16,5.01 8.36,7.66l0.82,2.29c-0.13,0.18 -0.25,0.38 -0.34,0.58c-0.83,-0.17 -1.58,-0.63 -2.06,-1.36C5.44,7.15 2,7.82 2,10.61c0,4.4 3.01,6.24 5.66,5.03l2.29,-0.82c0.18,0.13 0.38,0.25 0.58,0.34c-0.17,0.83 -0.63,1.58 -1.36,2.06C7.15,18.56 7.82,22 10.61,22c4.4,0 6.24,-3.01 5.03,-5.66l-0.82,-2.29c0.13,-0.18 0.25,-0.38 0.34,-0.58c0.83,0.17 1.58,0.63 2.06,1.36c1.34,2.01 4.77,1.34 4.77,-1.45C22,9 18.99,7.16 16.34,8.36zM12,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5c0.83,0 1.5,0.67 1.5,1.5C13.5,12.83 12.83,13.5 12,13.5zM10.24,5.22C10.74,4.44 11.89,4 13.39,4c0.79,0 0.71,0.86 0.34,1.11c-1.22,0.81 -2,2.06 -2.25,3.44c-0.21,0.03 -0.42,0.08 -0.62,0.15l-0.68,-1.88C10,6.42 9.86,5.81 10.24,5.22zM6.83,13.82c-0.4,0.18 -1.01,0.32 -1.61,-0.06C4.44,13.26 4,12.11 4,10.61c0,-0.79 0.86,-0.71 1.11,-0.34c0.81,1.22 2.06,2 3.44,2.25c0.03,0.21 0.08,0.42 0.15,0.62L6.83,13.82zM13.76,18.78c-0.5,0.77 -1.65,1.22 -3.15,1.22c-0.79,0 -0.71,-0.86 -0.34,-1.11c1.22,-0.81 2,-2.06 2.25,-3.44c0.21,-0.03 0.42,-0.08 0.62,-0.15l0.68,1.88C14,17.58 14.14,18.18 13.76,18.78zM18.89,13.73c-0.81,-1.22 -2.06,-2 -3.44,-2.25c-0.03,-0.21 -0.08,-0.42 -0.15,-0.62l1.88,-0.68c0.4,-0.18 1.01,-0.32 1.61,0.06c0.77,0.5 1.22,1.65 1.22,3.15C20,14.19 19.14,14.11 18.89,13.73z"
+        android:fillColor="@color/car_nav_icon_fill_color" />
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_hvac_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac_selected.xml
new file mode 100644
index 0000000..817b714
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_hvac_selected.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M16.34,8.36l-2.29,0.82c-0.18,-0.13 -0.38,-0.25 -0.58,-0.34c0.17,-0.83 0.63,-1.58 1.36,-2.06C16.85,5.44 16.18,2 13.39,2C9,2 7.16,5.01 8.36,7.66l0.82,2.29c-0.13,0.18 -0.25,0.38 -0.34,0.58c-0.83,-0.17 -1.58,-0.63 -2.06,-1.36C5.44,7.15 2,7.82 2,10.61c0,4.4 3.01,6.24 5.66,5.03l2.29,-0.82c0.18,0.13 0.38,0.25 0.58,0.34c-0.17,0.83 -0.63,1.58 -1.36,2.06C7.15,18.56 7.82,22 10.61,22c4.4,0 6.24,-3.01 5.03,-5.66l-0.82,-2.29c0.13,-0.18 0.25,-0.38 0.34,-0.58c0.83,0.17 1.58,0.63 2.06,1.36c1.34,2.01 4.77,1.34 4.77,-1.45C22,9 18.99,7.16 16.34,8.36zM12,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5c0.83,0 1.5,0.67 1.5,1.5C13.5,12.83 12.83,13.5 12,13.5zM10.24,5.22C10.74,4.44 11.89,4 13.39,4c0.79,0 0.71,0.86 0.34,1.11c-1.22,0.81 -2,2.06 -2.25,3.44c-0.21,0.03 -0.42,0.08 -0.62,0.15l-0.68,-1.88C10,6.42 9.86,5.81 10.24,5.22zM6.83,13.82c-0.4,0.18 -1.01,0.32 -1.61,-0.06C4.44,13.26 4,12.11 4,10.61c0,-0.79 0.86,-0.71 1.11,-0.34c0.81,1.22 2.06,2 3.44,2.25c0.03,0.21 0.08,0.42 0.15,0.62L6.83,13.82zM13.76,18.78c-0.5,0.77 -1.65,1.22 -3.15,1.22c-0.79,0 -0.71,-0.86 -0.34,-1.11c1.22,-0.81 2,-2.06 2.25,-3.44c0.21,-0.03 0.42,-0.08 0.62,-0.15l0.68,1.88C14,17.58 14.14,18.18 13.76,18.78zM18.89,13.73c-0.81,-1.22 -2.06,-2 -3.44,-2.25c-0.03,-0.21 -0.08,-0.42 -0.15,-0.62l1.88,-0.68c0.4,-0.18 1.01,-0.32 1.61,0.06c0.77,0.5 1.22,1.65 1.22,3.15C20,14.19 19.14,14.11 18.89,13.73z"
+        android:fillColor="@color/car_nav_icon_fill_color_selected" />
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification.xml b/packages/CarSystemUI/res/drawable/car_ic_notification.xml
index 9d9ad0f..aabf916 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_notification.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_notification.xml
@@ -15,10 +15,10 @@
   ~ limitations under the License
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="44"
-    android:viewportHeight="44"
-    android:width="44dp"
-    android:height="44dp">
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="44"
+        android:viewportHeight="44">
     <path
         android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
         android:fillColor="@color/car_nav_icon_fill_color" />
diff --git a/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
new file mode 100644
index 0000000..1195d05
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/system_bar_icon_drawing_size"
+    android:height="@dimen/system_bar_icon_drawing_size"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"
+      android:fillColor="@color/system_bar_user_icon_color"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/hvac_decrease_button.xml b/packages/CarSystemUI/res/drawable/hvac_decrease_button.xml
new file mode 100644
index 0000000..656e94a
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/hvac_decrease_button.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <item android:gravity="center"
+          android:width="@dimen/system_bar_icon_drawing_size"
+          android:height="@dimen/system_bar_icon_drawing_size">
+        <aapt:attr name="android:drawable">
+            <shape android:shape="oval">
+                <size
+                    android:width="@dimen/system_bar_icon_drawing_size"
+                    android:height="@dimen/system_bar_icon_drawing_size"/>
+                <solid
+                    android:color="@color/hvac_temperature_adjust_button_color"/>
+            </shape>
+        </aapt:attr>
+    </item>
+    <item android:gravity="center"
+          android:width="@dimen/system_bar_icon_drawing_size"
+          android:height="@dimen/system_bar_icon_drawing_size">
+        <aapt:attr name="android:drawable">
+            <vector android:width="@dimen/system_bar_icon_drawing_size"
+                    android:height="@dimen/system_bar_icon_drawing_size"
+                    android:viewportWidth="24.0"
+                    android:viewportHeight="24.0">
+                <path
+                    android:fillColor="@color/hvac_temperature_decrease_arrow_color"
+                    android:pathData="M14,7l-5,5 5,5V7z"/>
+            </vector>
+        </aapt:attr>
+    </item>
+    <item>
+        <aapt:attr name="android:drawable">
+            <ripple android:color="?android:attr/colorControlHighlight"/>
+        </aapt:attr>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/hvac_increase_button.xml b/packages/CarSystemUI/res/drawable/hvac_increase_button.xml
new file mode 100644
index 0000000..57c07c8
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/hvac_increase_button.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<layer-list
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <item android:gravity="center"
+          android:width="@dimen/system_bar_icon_drawing_size"
+          android:height="@dimen/system_bar_icon_drawing_size">
+        <aapt:attr name="android:drawable">
+            <shape android:shape="oval">
+                <size
+                    android:width="@dimen/system_bar_icon_drawing_size"
+                    android:height="@dimen/system_bar_icon_drawing_size"/>
+                <solid
+                    android:color="@color/hvac_temperature_adjust_button_color"/>
+            </shape>
+        </aapt:attr>
+    </item>
+    <item android:gravity="center"
+          android:width="@dimen/system_bar_icon_drawing_size"
+          android:height="@dimen/system_bar_icon_drawing_size">
+        <aapt:attr name="android:drawable">
+            <vector android:width="@dimen/system_bar_icon_drawing_size"
+                    android:height="@dimen/system_bar_icon_drawing_size"
+                    android:viewportWidth="24.0"
+                    android:viewportHeight="24.0">
+                <path
+                    android:fillColor="@color/hvac_temperature_increase_arrow_color"
+                    android:pathData="M10,17l5,-5 -5,-5v10z"/>
+            </vector>
+        </aapt:attr>
+    </item>
+    <item>
+        <aapt:attr name="android:drawable">
+            <ripple android:color="?android:attr/colorControlHighlight"/>
+        </aapt:attr>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
index 546b1a8..71fcc53 100644
--- a/packages/CarSystemUI/res/drawable/ic_mic_white.xml
+++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
@@ -15,11 +15,11 @@
   ~ limitations under the License
   -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:viewportWidth="44"
-    android:viewportHeight="44"
-    android:width="44dp"
-    android:height="44dp">
-    <path
+        android:width="@dimen/system_bar_icon_drawing_size"
+        android:height="@dimen/system_bar_icon_drawing_size"
+        android:viewportWidth="44"
+        android:viewportHeight="44">
+<path
         android:pathData="M22 25.6666667C25.0433333 25.6666667 27.4816667 23.21 27.4816667 20.1666667L27.5 9.16666667C27.5 6.12333333 25.0433333 3.66666667 22 3.66666667C18.9566667 3.66666667 16.5 6.12333333 16.5 9.16666667L16.5 20.1666667C16.5 23.21 18.9566667 25.6666667 22 25.6666667ZM31.7166667 20.1666667C31.7166667 25.6666667 27.06 29.5166667 22 29.5166667C16.94 29.5166667 12.2833333 25.6666667 12.2833333 20.1666667L9.16666667 20.1666667C9.16666667 26.4183333 14.1533333 31.5883333 20.1666667 32.4866667L20.1666667 38.5L23.8333333 38.5L23.8333333 32.4866667C29.8466667 31.6066667 34.8333333 26.4366667 34.8333333 20.1666667L31.7166667 20.1666667Z"
         android:fillColor="@color/car_nav_icon_fill_color" />
 </vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/adjustable_temperature_view.xml b/packages/CarSystemUI/res/layout/adjustable_temperature_view.xml
new file mode 100644
index 0000000..d197409
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/adjustable_temperature_view.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright (C) 2020 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="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="center_vertical"
+    android:paddingEnd="@dimen/hvac_container_padding"
+    android:paddingStart="@dimen/hvac_container_padding">
+
+    <ImageView
+        android:id="@+id/hvac_decrease_button"
+        android:layout_width="@dimen/hvac_temperature_button_size"
+        android:layout_height="@dimen/hvac_temperature_button_size"
+        android:scaleType="center"
+        android:src="@drawable/hvac_decrease_button"/>
+
+    <TextView
+        android:id="@+id/hvac_temperature_text"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:textSize="@dimen/hvac_temperature_text_size"
+        android:textColor="@color/system_bar_text_color"
+        android:paddingStart="@dimen/hvac_temperature_text_padding"
+        android:paddingEnd="@dimen/hvac_temperature_text_padding"
+        android:gravity="center"/>
+
+    <ImageView
+        android:id="@+id/hvac_increase_button"
+        android:layout_width="@dimen/hvac_temperature_button_size"
+        android:layout_height="@dimen/hvac_temperature_button_size"
+        android:scaleType="center"
+        android:src="@drawable/hvac_increase_button" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index a49a637..b07dde5 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2020 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.
@@ -12,7 +12,7 @@
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
+  ~ limitations under the License.
   -->
 
 <com.android.systemui.car.navigationbar.CarNavigationBarView
@@ -21,115 +21,115 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@drawable/system_bar_background"
-    android:orientation="vertical">
-    <!--The 20dp padding is the difference between the background selected icon size and the ripple
-        that was chosen, thus it's a hack to make it look pretty and not an official margin value-->
-    <LinearLayout
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <RelativeLayout
         android:id="@+id/nav_buttons"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:gravity="center"
-        android:layoutDirection="ltr"
-        android:paddingEnd="20dp"
-        android:paddingStart="20dp">
+        android:layoutDirection="ltr">
 
         <com.android.systemui.car.navigationbar.CarNavigationButton
-            android:id="@+id/home"
-            style="@style/NavigationBarButton"
-            systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
-            systemui:icon="@drawable/car_ic_overview"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-            systemui:selectedIcon="@drawable/car_ic_overview_selected"
-            systemui:highlightWhenSelected="true"
-        />
-
-        <Space
-            android:layout_width="0dp"
+            android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_weight="1"/>
+            android:layout_alignParentStart="true"
+            android:background="@null"
+            systemui:broadcast="true"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end">
+
+            <com.android.systemui.car.hvac.AdjustableTemperatureView
+                android:id="@+id/driver_hvac"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                systemui:hvacAreaId="49"
+                systemui:hvacTempFormat="%.0f\u00B0" />
+        </com.android.systemui.car.navigationbar.CarNavigationButton>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_centerInParent="true"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:layoutDirection="ltr"
+            android:paddingEnd="@dimen/system_bar_button_group_padding"
+            android:paddingStart="@dimen/system_bar_button_group_padding">
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/home"
+                style="@style/NavigationBarButton"
+                systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+                systemui:icon="@drawable/car_ic_home"
+                systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+                systemui:selectedIcon="@drawable/car_ic_home_selected"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/phone_nav"
+                style="@style/NavigationBarButton"
+                systemui:icon="@drawable/car_ic_phone"
+                systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
+                systemui:packages="com.android.car.dialer"
+                systemui:selectedIcon="@drawable/car_ic_phone_selected"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/grid_nav"
+                style="@style/NavigationBarButton"
+                systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+                systemui:icon="@drawable/car_ic_apps"
+                systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
+                systemui:selectedIcon="@drawable/car_ic_apps_selected"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/hvac"
+                style="@style/NavigationBarButton"
+                systemui:icon="@drawable/car_ic_hvac"
+                systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+                systemui:selectedIcon="@drawable/car_ic_hvac_selected"
+                systemui:broadcast="true"/>
+
+            <com.android.systemui.car.navigationbar.CarNavigationButton
+                android:id="@+id/notifications"
+                style="@style/NavigationBarButton"
+                systemui:icon="@drawable/car_ic_notification"
+                systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"/>
+
+            <com.android.systemui.car.navigationbar.AssitantButton
+                android:id="@+id/assist"
+                style="@style/NavigationBarButton"
+                systemui:icon="@drawable/ic_mic_white"
+                systemui:useDefaultAppIconForRole="true"/>
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"/>
+        </LinearLayout>
 
         <com.android.systemui.car.navigationbar.CarNavigationButton
-            android:id="@+id/maps_nav"
-            style="@style/NavigationBarButton"
-            systemui:categories="android.intent.category.APP_MAPS"
-            systemui:icon="@drawable/car_ic_navigation"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end"
-            systemui:selectedIcon="@drawable/car_ic_navigation_selected"
-            systemui:highlightWhenSelected="true"
-        />
-
-        <Space
-            android:layout_width="0dp"
+            android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_weight="1"/>
+            android:layout_alignParentEnd="true"
+            android:background="@null"
+            systemui:broadcast="true"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end">
 
-        <com.android.systemui.car.navigationbar.CarNavigationButton
-            android:id="@+id/music_nav"
-            style="@style/NavigationBarButton"
-            systemui:categories="android.intent.category.APP_MUSIC"
-            systemui:icon="@drawable/car_ic_music"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.MEDIA_TEMPLATE;launchFlags=0x10000000;end"
-            systemui:packages="com.android.car.media"
-            systemui:selectedIcon="@drawable/car_ic_music_selected"
-            systemui:highlightWhenSelected="true"
-        />
-
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"/>
-
-        <com.android.systemui.car.navigationbar.CarNavigationButton
-            android:id="@+id/phone_nav"
-            style="@style/NavigationBarButton"
-            systemui:icon="@drawable/car_ic_phone"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
-            systemui:packages="com.android.car.dialer"
-            systemui:selectedIcon="@drawable/car_ic_phone_selected"
-            systemui:highlightWhenSelected="true"
-        />
-
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"/>
-
-        <com.android.systemui.car.navigationbar.CarNavigationButton
-            android:id="@+id/grid_nav"
-            style="@style/NavigationBarButton"
-            systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
-            systemui:icon="@drawable/car_ic_apps"
-            systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
-            systemui:selectedIcon="@drawable/car_ic_apps_selected"
-            systemui:highlightWhenSelected="true"
-        />
-
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"/>
-
-        <com.android.systemui.car.navigationbar.CarNavigationButton
-            android:id="@+id/notifications"
-            style="@style/NavigationBarButton"
-            systemui:icon="@drawable/car_ic_notification"
-            systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"
-        />
-
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"/>
-
-        <com.android.systemui.car.navigationbar.AssitantButton
-            android:id="@+id/assist"
-            style="@style/NavigationBarButton"
-            systemui:icon="@drawable/ic_mic_white"
-            systemui:useDefaultAppIconForRole="true"
-        />
-
-    </LinearLayout>
+            <com.android.systemui.car.hvac.AdjustableTemperatureView
+                android:id="@+id/passenger_hvac"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_alignParentEnd="true"
+                android:gravity="center_vertical"
+                systemui:hvacAreaId="68"
+                systemui:hvacTempFormat="%.0f\u00B0" />
+        </com.android.systemui.car.navigationbar.CarNavigationButton>
+    </RelativeLayout>
 
     <LinearLayout
         android:id="@+id/lock_screen_nav_buttons"
@@ -142,5 +142,4 @@
         android:paddingStart="@dimen/car_keyline_1"
         android:visibility="gone"
     />
-
 </com.android.systemui.car.navigationbar.CarNavigationBarView>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index cdc29eec..af8482a8 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -31,39 +31,44 @@
         android:layoutDirection="ltr">
 
         <FrameLayout
-            android:id="@+id/left_hvac_container"
+            android:id="@+id/user_name_container"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_alignParentStart="true"
+            android:layout_toStartOf="@+id/clock_container"
         >
 
             <com.android.systemui.car.navigationbar.CarNavigationButton
-                android:id="@+id/hvacleft"
+                android:id="@+id/user_name"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:background="@null"
-                systemui:broadcast="true"
-                systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            />
-
-            <com.android.systemui.car.hvac.AnimatedTemperatureView
-                android:id="@+id/lefttext"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:paddingStart="@*android:dimen/car_padding_4"
-                android:paddingEnd="16dp"
-                android:gravity="center_vertical|start"
-                android:minEms="4"
-                android:textAppearance="@style/TextAppearance.CarStatus"
-                systemui:hvacAreaId="49"
-                systemui:hvacMaxText="@string/hvac_max_text"
-                systemui:hvacMaxValue="@dimen/hvac_max_value"
-                systemui:hvacMinText="@string/hvac_min_text"
-                systemui:hvacMinValue="@dimen/hvac_min_value"
-                systemui:hvacPivotOffset="60dp"
-                systemui:hvacPropertyId="358614275"
-                systemui:hvacTempFormat="%.0f\u00B0"
-            />
+                systemui:icon="@null"
+                systemui:intent="intent:#Intent;component=com.android.car.settings/.users.UserSwitcherActivity;launchFlags=0x24000000;end"
+            >
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:orientation="horizontal"
+                    android:gravity="center_vertical"
+                >
+                    <ImageView
+                        android:id="@+id/user_avatar"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        android:src="@drawable/car_ic_user_icon"
+                        android:paddingLeft="@dimen/system_bar_user_icon_padding"
+                        android:paddingRight="@dimen/system_bar_user_icon_padding"
+                    />
+                    <TextView
+                        android:id="@+id/user_name_text"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        android:gravity="center_vertical"
+                        android:textAppearance="@style/TextAppearance.SystemBar.Username"
+                        android:maxLines="1"
+                    />
+                </LinearLayout>
+            </com.android.systemui.car.navigationbar.CarNavigationButton>
         </FrameLayout>
 
         <FrameLayout
@@ -86,7 +91,8 @@
                 android:layout_gravity="center"
                 android:elevation="5dp"
                 android:singleLine="true"
-                android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+                android:textAppearance="@style/TextAppearance.SystemBar.Clock"
+                systemui:amPmStyle="normal"
             />
         </FrameLayout>
 
@@ -94,10 +100,9 @@
             android:id="@+id/system_icon_area"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_centerHorizontal="true"
+            android:layout_alignParentEnd="true"
             android:layout_centerVertical="true"
-            android:layout_toEndOf="@+id/clock_container"
-            android:paddingStart="@*android:dimen/car_padding_1"
+            android:paddingEnd="@*android:dimen/car_padding_1"
             android:gravity="center_vertical"
             android:orientation="horizontal"
         >
@@ -107,46 +112,9 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
-                android:paddingStart="4dp"
                 android:gravity="center_vertical"
             />
         </LinearLayout>
-
-        <FrameLayout
-            android:id="@+id/right_hvac_container"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_alignParentEnd="true"
-        >
-
-            <com.android.systemui.car.navigationbar.CarNavigationButton
-                android:id="@+id/hvacright"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:background="@null"
-                systemui:broadcast="true"
-                systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            />
-
-            <com.android.systemui.car.hvac.AnimatedTemperatureView
-                android:id="@+id/righttext"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:paddingStart="16dp"
-                android:paddingEnd="@*android:dimen/car_padding_4"
-                android:gravity="center_vertical|end"
-                android:minEms="4"
-                android:textAppearance="@style/TextAppearance.CarStatus"
-                systemui:hvacAreaId="68"
-                systemui:hvacMaxText="@string/hvac_max_text"
-                systemui:hvacMaxValue="@dimen/hvac_max_value"
-                systemui:hvacMinText="@string/hvac_min_text"
-                systemui:hvacMinValue="@dimen/hvac_min_value"
-                systemui:hvacPivotOffset="60dp"
-                systemui:hvacPropertyId="358614275"
-                systemui:hvacTempFormat="%.0f\u00B0"
-            />
-        </FrameLayout>
     </RelativeLayout>
 
 </com.android.systemui.car.navigationbar.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
index 9634950..b8ac2b4 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -55,13 +55,7 @@
           android:minEms="4"
           android:textAppearance="@style/TextAppearance.CarStatus"
           systemui:hvacAreaId="49"
-          systemui:hvacMaxText="@string/hvac_max_text"
-          systemui:hvacMaxValue="@dimen/hvac_max_value"
-          systemui:hvacMinText="@string/hvac_min_text"
-          systemui:hvacMinValue="@dimen/hvac_min_value"
           systemui:hvacPivotOffset="60dp"
-          systemui:hvacPropertyId="358614275"
-          systemui:hvacTempFormat="%.0f\u00B0"
           />
     </FrameLayout>
 
@@ -133,13 +127,7 @@
           android:minEms="4"
           android:textAppearance="@style/TextAppearance.CarStatus"
           systemui:hvacAreaId="68"
-          systemui:hvacMaxText="@string/hvac_max_text"
-          systemui:hvacMaxValue="@dimen/hvac_max_value"
-          systemui:hvacMinText="@string/hvac_min_text"
-          systemui:hvacMinValue="@dimen/hvac_min_value"
           systemui:hvacPivotOffset="60dp"
-          systemui:hvacPropertyId="358614275"
-          systemui:hvacTempFormat="%.0f\u00B0"
           />
     </FrameLayout>
   </RelativeLayout>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index ab94265..c390cc8 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -31,9 +31,17 @@
     <color name="docked_divider_background">@*android:color/car_grey_50</color>
     <color name="system_bar_background_opaque">#ff172026</color>
 
+    <!-- colors for status bar -->
+    <color name="system_bar_user_icon_color">#ffffff</color>
+    <color name="system_bar_text_color">#ffffff</color>
     <color name="status_bar_background_color">#33000000</color>
     <drawable name="system_bar_background">@color/status_bar_background_color</drawable>
 
+    <!-- colors for hvac temperature view -->
+    <color name="hvac_temperature_adjust_button_color">#3C4043</color>
+    <color name="hvac_temperature_decrease_arrow_color">#8AB4F8</color>
+    <color name="hvac_temperature_increase_arrow_color">#F28B82</color>
+
     <!-- The background color of the notification shade -->
     <color name="notification_shade_background_color">#D6000000</color>
 
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index d3277de..1fe2760 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -123,7 +123,6 @@
         <item>com.android.systemui.volume.VolumeUI</item>
         <item>com.android.systemui.statusbar.phone.StatusBar</item>
         <item>com.android.systemui.keyboard.KeyboardUI</item>
-        <item>com.android.systemui.pip.PipUI</item>
         <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
         <item>com.android.systemui.LatencyTester</item>
         <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 8359dac..28b8ead 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -46,16 +46,29 @@
        in frameworks/base/core package and thus will have no effect if
        set here. See car_product overlay for car specific defaults-->
 
-    <dimen name="status_bar_icon_drawing_size_dark">36dp</dimen>
-    <dimen name="status_bar_icon_drawing_size">36dp</dimen>
+    <dimen name="system_bar_user_icon_padding">16dp</dimen>
+    <dimen name="system_bar_user_icon_drawing_size">36dp</dimen>
+    <!-- Padding on either side of the group of all system bar buttons -->
+    <dimen name="system_bar_button_group_padding">64dp</dimen>
+    <dimen name="system_bar_icon_drawing_size">44dp</dimen>
+    <dimen name="system_bar_button_size">76dp</dimen>
+    <!-- Margin between the system bar buttons -->
+    <dimen name="system_bar_button_margin">32dp</dimen>
+    <!-- Padding between the system bar button and the icon within it -->
+    <dimen name="system_bar_button_padding">16dp</dimen>
+
     <!-- The amount by which to scale up the status bar icons. -->
     <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.75</item>
 
     <dimen name="car_primary_icon_size">@*android:dimen/car_primary_icon_size</dimen>
 
+    <dimen name="hvac_container_padding">16dp</dimen>
+    <dimen name="hvac_temperature_text_size">56sp</dimen>
+    <dimen name="hvac_temperature_text_padding">8dp</dimen>
+    <dimen name="hvac_temperature_button_size">76dp</dimen>
     <!--These values represent MIN and MAX for hvac-->
-    <item name="hvac_min_value" format="float" type="dimen">0</item>
-    <item name="hvac_max_value" format="float" type="dimen">126</item>
+    <item name="hvac_min_value_celsius" format="float" type="dimen">10</item>
+    <item name="hvac_max_value_celsius" format="float" type="dimen">35</item>
 
     <!-- Largest size an avatar might need to be drawn in the user picker, status bar, or
          quick settings header -->
diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml
index 67fd5bb..fbdb516 100644
--- a/packages/CarSystemUI/res/values/strings.xml
+++ b/packages/CarSystemUI/res/values/strings.xml
@@ -16,6 +16,8 @@
   -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Format for HVAC temperature (No decimal and the degree symbol) -->
+    <string name="hvac_temperature_format" translatable="false">%.0f\u00B0</string>
     <!-- String to represent lowest setting of an HVAC system [CHAR LIMIT=10]-->
     <string name="hvac_min_text">Min</string>
     <!-- String to represent largest setting of an HVAC system [CHAR LIMIT=10]-->
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index e76373d..f242db0 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -25,21 +25,29 @@
         <item name="android:padding">22dp</item>
     </style>
 
-    <style name="TextAppearance.StatusBar.Clock"
-        parent="@*android:style/TextAppearance.StatusBar.Icon">
-        <item name="android:textSize">42sp</item>
-        <item name="android:fontFamily">sans-serif-regular</item>
-        <item name="android:textColor">@*android:color/car_grey_50</item>
+    <style name="TextAppearance.SystemBar.Clock"
+           parent="@*android:style/TextAppearance.StatusBar.Icon">
+        <item name="android:textSize">@dimen/car_body1_size</item>
+        <item name="android:textColor">@*android:color/car_headline3</item>
+    </style>
+
+    <style name="TextAppearance.SystemBar.Username"
+           parent="@android:style/TextAppearance.DeviceDefault">
+        <item name="android:textSize">@dimen/car_body3_size</item>
+        <item name="android:textColor">@color/system_bar_text_color</item>
     </style>
 
     <style name="TextAppearance.CarStatus" parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textSize">@*android:dimen/car_body2_size</item>
-        <item name="android:textColor">@*android:color/car_grey_50</item>
+        <item name="android:textColor">@color/system_bar_text_color</item>
     </style>
 
     <style name="NavigationBarButton">
-        <item name="android:layout_height">96dp</item>
-        <item name="android:layout_width">96dp</item>
+        <item name="android:layout_height">@dimen/system_bar_button_size</item>
+        <item name="android:layout_width">@dimen/system_bar_button_size</item>
+        <item name="android:layout_marginEnd">@dimen/system_bar_button_margin</item>
+        <item name="android:padding">@dimen/system_bar_button_padding</item>
+        <item name="android:gravity">center</item>
         <item name="android:background">?android:attr/selectableItemBackground</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
index 9039671..51855dc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
@@ -16,12 +16,10 @@
 
 package com.android.systemui;
 
-import com.android.systemui.dagger.DependencyBinder;
 import com.android.systemui.dagger.DependencyProvider;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.SystemUIModule;
-import com.android.systemui.pip.phone.dagger.PipModule;
 
 import dagger.Subcomponent;
 
@@ -32,8 +30,6 @@
 @Subcomponent(modules = {
         CarComponentBinder.class,
         DependencyProvider.class,
-        DependencyBinder.class,
-        PipModule.class,
         SystemUIModule.class,
         CarSystemUIModule.class,
         CarSystemUIBinder.class})
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 3971e18..3def945 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -28,8 +28,6 @@
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
-import com.android.systemui.onehanded.OneHandedUI;
-import com.android.systemui.pip.PipUI;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsModule;
@@ -95,18 +93,6 @@
     @ClassKey(LatencyTester.class)
     public abstract SystemUI bindLatencyTester(LatencyTester sysui);
 
-    /** Inject into OneHandedUI. */
-    @Binds
-    @IntoMap
-    @ClassKey(OneHandedUI.class)
-    public abstract SystemUI bindOneHandedUI(OneHandedUI sysui);
-
-    /** Inject into PipUI. */
-    @Binds
-    @IntoMap
-    @ClassKey(PipUI.class)
-    public abstract SystemUI bindPipUI(PipUI sysui);
-
     /** Inject into PowerUI. */
     @Binds
     @IntoMap
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 3eea513..51fda96 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -52,10 +52,10 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.ShadeControllerImpl;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -92,7 +92,7 @@
             Context context,
             StatusBarStateController statusBarStateController,
             KeyguardBypassController bypassController,
-            NotificationGroupManager groupManager,
+            GroupMembershipManager groupManager,
             ConfigurationController configurationController) {
         return new HeadsUpManagerPhone(context, statusBarStateController, bypassController,
                 groupManager, configurationController);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java
index a2ba880..fef0324 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java
@@ -16,20 +16,19 @@
 
 package com.android.systemui.car;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.car.settings.CarSettings;
-import android.content.ContentResolver;
-import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
-import android.provider.Settings;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.settings.SecureSettings;
 
 import javax.inject.Inject;
 
@@ -40,30 +39,33 @@
 @SysUISingleton
 public class CarDeviceProvisionedControllerImpl extends DeviceProvisionedControllerImpl implements
         CarDeviceProvisionedController {
-    private static final Uri USER_SETUP_IN_PROGRESS_URI = Settings.Secure.getUriFor(
-            CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS);
-    private final ContentObserver mCarSettingsObserver = new ContentObserver(
-            Dependency.get(Dependency.MAIN_HANDLER)) {
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int flags) {
-            if (USER_SETUP_IN_PROGRESS_URI.equals(uri)) {
-                notifyUserSetupInProgressChanged();
-            }
-        }
-    };
-    private final ContentResolver mContentResolver;
+    private final Uri mUserSetupInProgressUri;
+    private final ContentObserver mCarSettingsObserver;
+    private final Handler mMainHandler;
+    private final SecureSettings mSecureSettings;
 
     @Inject
-    public CarDeviceProvisionedControllerImpl(Context context, @Main Handler mainHandler,
-            BroadcastDispatcher broadcastDispatcher) {
-        super(context, mainHandler, broadcastDispatcher);
-        mContentResolver = context.getContentResolver();
+    public CarDeviceProvisionedControllerImpl(@Main Handler mainHandler,
+            BroadcastDispatcher broadcastDispatcher, GlobalSettings globalSetting,
+            SecureSettings secureSettings) {
+        super(mainHandler, broadcastDispatcher, globalSetting, secureSettings);
+        mMainHandler = mainHandler;
+        mSecureSettings = secureSettings;
+        mUserSetupInProgressUri = mSecureSettings.getUriFor(
+                CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS);
+        mCarSettingsObserver = new ContentObserver(mMainHandler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri, int flags) {
+                if (mUserSetupInProgressUri.equals(uri)) {
+                    notifyUserSetupInProgressChanged();
+                }
+            }
+        };
     }
 
     @Override
     public boolean isUserSetupInProgress(int user) {
-        return Settings.Secure.getIntForUser(mContentResolver,
+        return mSecureSettings.getIntForUser(
                 CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS, /* def= */ 0, user) != 0;
     }
 
@@ -73,7 +75,7 @@
     }
 
     @Override
-    public void addCallback(DeviceProvisionedListener listener) {
+    public void addCallback(@NonNull DeviceProvisionedListener listener) {
         super.addCallback(listener);
         if (listener instanceof CarDeviceProvisionedListener) {
             ((CarDeviceProvisionedListener) listener).onUserSetupInProgressChanged();
@@ -82,9 +84,9 @@
 
     @Override
     protected void startListening(int user) {
-        mContentResolver.registerContentObserver(
-                USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver,
-                user);
+        mSecureSettings.registerContentObserverForUser(
+                mUserSetupInProgressUri, /* notifyForDescendants= */ true,
+                mCarSettingsObserver, user);
         // The SUW Flag observer is registered before super.startListening() so that the observer is
         // in place before DeviceProvisionedController starts to track user switches which avoids
         // an edge case where our observer gets registered twice.
@@ -94,16 +96,16 @@
     @Override
     protected void stopListening() {
         super.stopListening();
-        mContentResolver.unregisterContentObserver(mCarSettingsObserver);
+        mSecureSettings.unregisterContentObserver(mCarSettingsObserver);
     }
 
     @Override
     public void onUserSwitched(int newUserId) {
         super.onUserSwitched(newUserId);
-        mContentResolver.unregisterContentObserver(mCarSettingsObserver);
-        mContentResolver.registerContentObserver(
-                USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver,
-                newUserId);
+        mSecureSettings.unregisterContentObserver(mCarSettingsObserver);
+        mSecureSettings.registerContentObserverForUser(
+                mUserSetupInProgressUri, /* notifyForDescendants= */ true,
+                mCarSettingsObserver, newUserId);
     }
 
     private void notifyUserSetupInProgressChanged() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
new file mode 100644
index 0000000..85d4ceb
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 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.car.hvac;
+
+import static com.android.systemui.car.hvac.HvacController.convertToCelsius;
+import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+/**
+ * Displays temperature with a button to decrease and a button to increase on either side.
+ * Properties configured in the XML:
+ * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+ */
+public class AdjustableTemperatureView extends LinearLayout implements TemperatureView {
+
+    private final int mAreaId;
+    private TextView mTempTextView;
+    private float mMinTempC;
+    private float mMaxTempC;
+    private String mTempFormat;
+    private boolean mDisplayInFahrenheit = false;
+
+    private HvacController mHvacController;
+    private float mCurrentTempC;
+
+    public AdjustableTemperatureView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
+        mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        LayoutInflater.from(getContext()).inflate(R.layout.adjustable_temperature_view,
+                /* root= */ this);
+        mTempFormat = getResources().getString(R.string.hvac_temperature_format);
+        mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius);
+        mMaxTempC = getResources().getFloat(R.dimen.hvac_max_value_celsius);
+        initializeButtons();
+    }
+
+    @Override
+    public void setHvacController(HvacController controller) {
+        mHvacController = controller;
+    }
+
+    @Override
+    public void setTemp(float tempC) {
+        if (tempC > mMaxTempC || tempC < mMinTempC) {
+            return;
+        }
+        if (mTempTextView == null) {
+            mTempTextView = findViewById(R.id.hvac_temperature_text);
+        }
+        mTempTextView.setText(String.format(mTempFormat,
+                mDisplayInFahrenheit ? convertToFahrenheit(tempC) : tempC));
+        mCurrentTempC = tempC;
+    }
+
+    @Override
+    public void setDisplayInFahrenheit(boolean displayFahrenheit) {
+        mDisplayInFahrenheit = displayFahrenheit;
+        setTemp(mCurrentTempC);
+    }
+
+    @Override
+    public int getAreaId() {
+        return mAreaId;
+    }
+
+    private void initializeButtons() {
+        findViewById(R.id.hvac_decrease_button).setOnClickListener(v -> {
+            float newTemp = mDisplayInFahrenheit ? convertToCelsius(
+                    convertToFahrenheit(mCurrentTempC) - 1) : (mCurrentTempC - 1);
+            setTemperature(newTemp, mAreaId);
+        });
+
+        findViewById(R.id.hvac_increase_button).setOnClickListener(v -> {
+            float newTemp = mDisplayInFahrenheit ? convertToCelsius(
+                    convertToFahrenheit(mCurrentTempC) + 1) : (mCurrentTempC + 1);
+            setTemperature(newTemp, mAreaId);
+        });
+    }
+
+    private void setTemperature(float tempC, int zone) {
+        if (tempC < mMaxTempC && tempC > mMinTempC && mHvacController != null) {
+            mHvacController.setTemperature(tempC, zone);
+        }
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java
index a729431..b98b680 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.car.hvac;
 
+import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit;
+
 import android.animation.ObjectAnimator;
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -40,9 +42,7 @@
  * Simple text display of HVAC properties, It is designed to show mTemperature and is configured in
  * the XML.
  * XML properties:
- * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
- * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
- * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol)
+ * hvacAreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1)
  * hvacOrientaion = Example: left
  */
 public class AnimatedTemperatureView extends FrameLayout implements TemperatureView {
@@ -84,7 +84,6 @@
     }
 
     private final int mAreaId;
-    private final int mPropertyId;
     private final int mPivotOffset;
     private final int mGravity;
     private final int mTextAppearanceRes;
@@ -100,12 +99,13 @@
     private final TemperatureTextAnimator mTextAnimator;
     boolean mDisplayInFahrenheit = false;
 
+    private HvacController mHvacController;
+
     public AnimatedTemperatureView(Context context, AttributeSet attrs) {
         super(context, attrs);
         TypedArray typedArray = context.obtainStyledAttributes(attrs,
                 R.styleable.AnimatedTemperatureView);
         mAreaId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacAreaId, -1);
-        mPropertyId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacPropertyId, -1);
         mPivotOffset =
                 typedArray.getDimensionPixelOffset(
                         R.styleable.AnimatedTemperatureView_hvacPivotOffset, 0);
@@ -115,11 +115,8 @@
                 typedArray.getResourceId(R.styleable.AnimatedTemperatureView_android_textAppearance,
                         0);
         mMinEms = typedArray.getInteger(R.styleable.AnimatedTemperatureView_android_minEms, 0);
-        mMinValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMinValue,
-                Float.NaN);
-        mMaxValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMaxValue,
-                Float.NaN);
-
+        mMinValue = getResources().getFloat(R.dimen.hvac_min_value_celsius);
+        mMaxValue = getResources().getFloat(R.dimen.hvac_max_value_celsius);
 
         mPaddingRect =
                 new Rect(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
@@ -138,15 +135,10 @@
 
         mBackgroundAnimator = new TemperatureBackgroundAnimator(this, background);
 
-
-        String format = typedArray.getString(R.styleable.AnimatedTemperatureView_hvacTempFormat);
-        format = (format == null) ? "%.1f\u00B0" : format;
-        CharSequence minText = typedArray.getString(
-                R.styleable.AnimatedTemperatureView_hvacMinText);
-        CharSequence maxText = typedArray.getString(
-                R.styleable.AnimatedTemperatureView_hvacMaxText);
-        mTextAnimator = new TemperatureTextAnimator(this, textSwitcher, format, mPivotOffset,
-                minText, maxText);
+        mTextAnimator = new TemperatureTextAnimator(this, textSwitcher,
+                getResources().getString(R.string.hvac_temperature_format), mPivotOffset,
+                getResources().getString(R.string.hvac_min_text),
+                getResources().getString(R.string.hvac_max_text));
 
         addView(background, ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT);
@@ -186,6 +178,11 @@
         return textView;
     }
 
+    @Override
+    public void setHvacController(HvacController controller) {
+        mHvacController = controller;
+    }
+
     /**
      * Formats the float for display
      *
@@ -252,15 +249,7 @@
     }
 
     /**
-     * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (358614275)
-     */
-    @Override
-    public int getPropertyId() {
-        return mPropertyId;
-    }
-
-    /**
-     * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+     * @return hvac AreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1)
      */
     @Override
     public int getAreaId() {
@@ -272,6 +261,5 @@
         super.onDetachedFromWindow();
         mBackgroundAnimator.stopAnimations();
     }
-
 }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java
index a4b6bfc..10a361c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java
@@ -18,27 +18,27 @@
 
 import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
 
 import android.car.Car;
 import android.car.VehicleUnit;
 import android.car.hardware.CarPropertyValue;
-import android.car.hardware.hvac.CarHvacManager;
-import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
+import android.car.hardware.property.CarPropertyManager;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.systemui.car.CarServiceProvider;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.UiBackground;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -51,52 +51,68 @@
     public static final String TAG = "HvacController";
     private static final boolean DEBUG = true;
 
+    private final Executor mBackgroundExecutor;
     private final CarServiceProvider mCarServiceProvider;
     private final Set<TemperatureView> mRegisteredViews = new HashSet<>();
 
-    private CarHvacManager mHvacManager;
-    private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
+    private CarPropertyManager mCarPropertyManager;
+    private HashMap<Integer, List<TemperatureView>> mTempComponents = new HashMap<>();
 
-    /**
-     * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
-     * match.
-     */
-    private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() {
-        @Override
-        public void onChangeEvent(final CarPropertyValue val) {
-            try {
-                int areaId = val.getAreaId();
-                int propertyId = val.getPropertyId();
-                List<TemperatureView> temperatureViews = mTempComponents.get(
-                        new HvacKey(propertyId, areaId));
-                if (temperatureViews != null && !temperatureViews.isEmpty()) {
-                    float value = (float) val.getValue();
-                    if (DEBUG) {
-                        Log.d(TAG, "onChangeEvent: " + areaId + ":" + propertyId + ":" + value);
+    private final CarPropertyManager.CarPropertyEventCallback mHvacTemperatureSetCallback =
+            new CarPropertyManager.CarPropertyEventCallback() {
+                @Override
+                public void onChangeEvent(CarPropertyValue value) {
+                    try {
+                        int areaId = value.getAreaId();
+                        List<TemperatureView> temperatureViews = mTempComponents.get(areaId);
+                        if (temperatureViews != null && !temperatureViews.isEmpty()) {
+                            float newTemp = (float) value.getValue();
+                            if (DEBUG) {
+                                Log.d(TAG, "onChangeEvent: " + areaId + ":" + value);
+                            }
+                            for (TemperatureView view : temperatureViews) {
+                                view.setTemp(newTemp);
+                            }
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Failed handling hvac change event", e);
                     }
-                    for (TemperatureView tempView : temperatureViews) {
-                        tempView.setTemp(value);
-                    }
-                } // else the data is not of interest
-            } catch (Exception e) {
-                // catch all so we don't take down the sysui if a new data type is
-                // introduced.
-                Log.e(TAG, "Failed handling hvac change event", e);
-            }
-        }
+                }
 
-        @Override
-        public void onErrorEvent(final int propertyId, final int zone) {
-            Log.d(TAG, "HVAC error event, propertyId: " + propertyId
-                    + " zone: " + zone);
-        }
-    };
+                @Override
+                public void onErrorEvent(int propId, int zone) {
+                    Log.d(TAG, "HVAC error event, propertyId: " + propId + " zone: " + zone);
+                }
+            };
+
+    private final CarPropertyManager.CarPropertyEventCallback mTemperatureUnitChangeCallback =
+            new CarPropertyManager.CarPropertyEventCallback() {
+                @Override
+                public void onChangeEvent(CarPropertyValue value) {
+                    if (!mRegisteredViews.isEmpty()) {
+                        for (TemperatureView view : mRegisteredViews) {
+                            view.setDisplayInFahrenheit(
+                                    value.getValue().equals(VehicleUnit.FAHRENHEIT));
+                        }
+                    }
+                }
+
+                @Override
+                public void onErrorEvent(int propId, int zone) {
+                    Log.d(TAG, "HVAC error event, propertyId: " + propId + " zone: " + zone);
+                }
+            };
 
     private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceLifecycleListener =
             car -> {
                 try {
-                    mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE);
-                    mHvacManager.registerCallback(mHardwareCallback);
+                    mCarPropertyManager = (CarPropertyManager) car.getCarManager(
+                            Car.PROPERTY_SERVICE);
+                    mCarPropertyManager.registerCallback(mHvacTemperatureSetCallback,
+                            HVAC_TEMPERATURE_SET, CarPropertyManager.SENSOR_RATE_ONCHANGE);
+                    mCarPropertyManager.registerCallback(mTemperatureUnitChangeCallback,
+                            HVAC_TEMPERATURE_DISPLAY_UNITS,
+                            CarPropertyManager.SENSOR_RATE_ONCHANGE);
                     initComponents();
                 } catch (Exception e) {
                     Log.e(TAG, "Failed to correctly connect to HVAC", e);
@@ -104,13 +120,14 @@
             };
 
     @Inject
-    public HvacController(CarServiceProvider carServiceProvider) {
+    public HvacController(CarServiceProvider carServiceProvider,
+            @UiBackground Executor backgroundExecutor) {
         mCarServiceProvider = carServiceProvider;
+        mBackgroundExecutor = backgroundExecutor;
     }
 
     /**
-     * Create connection to the Car service. Note: call backs from the Car service
-     * ({@link CarHvacManager}) will happen on the same thread this method was called from.
+     * Create connection to the Car service.
      */
     public void connectToCarService() {
         mCarServiceProvider.addListener(mCarServiceLifecycleListener);
@@ -124,21 +141,18 @@
             return;
         }
 
-        HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
-        if (!mTempComponents.containsKey(hvacKey)) {
-            mTempComponents.put(hvacKey, new ArrayList<>());
+        int areaId = temperatureView.getAreaId();
+        if (!mTempComponents.containsKey(areaId)) {
+            mTempComponents.put(areaId, new ArrayList<>());
         }
-        mTempComponents.get(hvacKey).add(temperatureView);
+        mTempComponents.get(areaId).add(temperatureView);
         initComponent(temperatureView);
 
         mRegisteredViews.add(temperatureView);
     }
 
     private void initComponents() {
-        Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
-                mTempComponents.entrySet().iterator();
-        while (iterator.hasNext()) {
-            Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
+        for (Map.Entry<Integer, List<TemperatureView>> next : mTempComponents.entrySet()) {
             List<TemperatureView> temperatureViews = next.getValue();
             for (TemperatureView view : temperatureViews) {
                 initComponent(view);
@@ -147,27 +161,27 @@
     }
 
     private void initComponent(TemperatureView view) {
-        int id = view.getPropertyId();
         int zone = view.getAreaId();
         if (DEBUG) {
-            Log.d(TAG, "initComponent: " + zone + ":" + id);
+            Log.d(TAG, "initComponent: " + zone);
         }
 
         try {
-            if (mHvacManager != null
-                    && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
-                            VEHICLE_AREA_TYPE_GLOBAL)) {
-                if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+            if (mCarPropertyManager != null && mCarPropertyManager.isPropertyAvailable(
+                    HVAC_TEMPERATURE_DISPLAY_UNITS, VEHICLE_AREA_TYPE_GLOBAL)) {
+                if (mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
                         VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) {
                     view.setDisplayInFahrenheit(true);
                 }
-
             }
-            if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
+            if (mCarPropertyManager == null || !mCarPropertyManager.isPropertyAvailable(
+                    HVAC_TEMPERATURE_SET, zone)) {
                 view.setTemp(Float.NaN);
                 return;
             }
-            view.setTemp(mHvacManager.getFloatProperty(id, zone));
+            view.setTemp(
+                    mCarPropertyManager.getFloatProperty(HVAC_TEMPERATURE_SET, zone));
+            view.setHvacController(this);
         } catch (Exception e) {
             view.setTemp(Float.NaN);
             Log.e(TAG, "Failed to get value from hvac service", e);
@@ -199,30 +213,33 @@
     }
 
     /**
-     * Key for storing {@link TemperatureView}s in a hash map
+     * Set the temperature in Celsius of the specified zone
      */
-    private static class HvacKey {
-
-        int mPropertyId;
-        int mAreaId;
-
-        private HvacKey(int propertyId, int areaId) {
-            mPropertyId = propertyId;
-            mAreaId = areaId;
+    public void setTemperature(float tempC, int zone) {
+        if (mCarPropertyManager != null) {
+            // Internally, all temperatures are represented in floating point Celsius
+            mBackgroundExecutor.execute(
+                    () -> mCarPropertyManager.setFloatProperty(HVAC_TEMPERATURE_SET, zone, tempC));
         }
+    }
 
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            HvacKey hvacKey = (HvacKey) o;
-            return mPropertyId == hvacKey.mPropertyId
-                    && mAreaId == hvacKey.mAreaId;
-        }
+    /**
+     * Convert the given temperature in Celsius into Fahrenheit
+     *
+     * @param tempC - The temperature in Celsius
+     * @return Temperature in Fahrenheit.
+     */
+    public static float convertToFahrenheit(float tempC) {
+        return (tempC * 9f / 5f) + 32;
+    }
 
-        @Override
-        public int hashCode() {
-            return Objects.hash(mPropertyId, mAreaId);
-        }
+    /**
+     * Convert the given temperature in Fahrenheit to Celsius
+     *
+     * @param tempF - The temperature in Fahrenheit.
+     * @return Temperature in Celsius.
+     */
+    public static float convertToCelsius(float tempF) {
+        return (tempF - 32) * 5f / 9f;
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java
index 521a665..90df15c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.car.hvac;
 
+import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
@@ -27,24 +29,25 @@
  * Simple text display of HVAC properties, It is designed to show temperature and is configured in
  * the XML.
  * XML properties:
- * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
- * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
- * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol)
+ * hvacAreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1)
  */
 public class TemperatureTextView extends TextView implements TemperatureView {
 
     private final int mAreaId;
-    private final int mPropertyId;
     private final String mTempFormat;
+    private HvacController mHvacController;
     private boolean mDisplayFahrenheit = false;
 
     public TemperatureTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
         mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1);
-        mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
-        String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
-        mTempFormat = (format == null) ? "%.1f\u00B0" : format;
+        mTempFormat = getResources().getString(R.string.hvac_temperature_format);
+    }
+
+    @Override
+    public void setHvacController(HvacController controller) {
+        mHvacController = controller;
     }
 
     /**
@@ -70,15 +73,7 @@
     }
 
     /**
-     * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
-     */
-    @Override
-    public int getPropertyId() {
-        return mPropertyId;
-    }
-
-    /**
-     * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+     * @return hvac AreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1)
      */
     @Override
     public int getAreaId() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java
index 6b903fa..6edf254 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java
@@ -17,9 +17,17 @@
 package com.android.systemui.car.hvac;
 
 /**
- * Interface for Views that display temperature HVAC properties
+ * Interface for Views that display temperature HVAC properties.
  */
 public interface TemperatureView {
+
+    /**
+     * Sets the {@link HvacController} to handle changes to HVAC properties. The View is only
+     * responsible for the UI to display temperature. It should not contain logic that makes direct
+     * changes to HVAC properties and instead use this {@link HvacController}.
+     */
+    void setHvacController(HvacController controller);
+
     /**
      * Formats the float for display
      *
@@ -35,22 +43,7 @@
     void setDisplayInFahrenheit(boolean displayFahrenheit);
 
     /**
-     * Convert the given temperature in Celsius into Fahrenheit
-     *
-     * @param realTemp - The temperature in Celsius
-     * @return Temperature in Fahrenheit.
-     */
-    default float convertToFahrenheit(float realTemp) {
-        return (realTemp * 9f / 5f) + 32;
-    }
-
-    /**
-     * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
-     */
-    int getPropertyId();
-
-    /**
-     * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+     * @return hvac AreaId - Example: VehicleAreaSeat.SEAT_ROW_1_LEFT (1)
      */
     int getAreaId();
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 276ddfb..dadbc22 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -18,7 +18,6 @@
 
 import android.car.Car;
 import android.car.user.CarUserManager;
-import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
@@ -28,20 +27,17 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.car.CarServiceProvider;
 import com.android.systemui.car.navigationbar.CarNavigationBarController;
 import com.android.systemui.car.window.OverlayViewController;
 import com.android.systemui.car.window.OverlayViewGlobalStateController;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -63,18 +59,14 @@
     private static final String TAG = "CarKeyguardViewController";
     private static final boolean DEBUG = true;
 
-    private final Context mContext;
     private final Handler mHandler;
     private final CarServiceProvider mCarServiceProvider;
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
-    private final LockPatternUtils mLockPatternUtils;
-    private final FalsingManager mFalsingManager;
-    private final Lazy<KeyguardBypassController> mKeyguardBypassControllerLazy;
-    private final DismissCallbackRegistry mDismissCallbackRegistry;
     private final ViewMediatorCallback mViewMediatorCallback;
     private final CarNavigationBarController mCarNavigationBarController;
+    private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
     // Needed to instantiate mBouncer.
     private final KeyguardBouncer.BouncerExpansionCallback
             mExpansionCallback = new KeyguardBouncer.BouncerExpansionCallback() {
@@ -107,7 +99,6 @@
 
     @Inject
     public CarKeyguardViewController(
-            Context context,
             @Main Handler mainHandler,
             CarServiceProvider carServiceProvider,
             OverlayViewGlobalStateController overlayViewGlobalStateController,
@@ -116,26 +107,18 @@
             Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
             ViewMediatorCallback viewMediatorCallback,
             CarNavigationBarController carNavigationBarController,
-            /* The params below are only used to reuse KeyguardBouncer */
-            LockPatternUtils lockPatternUtils,
-            DismissCallbackRegistry dismissCallbackRegistry,
-            FalsingManager falsingManager,
-            Lazy<KeyguardBypassController> keyguardBypassControllerLazy) {
+            KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
 
         super(R.id.keyguard_stub, overlayViewGlobalStateController);
 
-        mContext = context;
         mHandler = mainHandler;
         mCarServiceProvider = carServiceProvider;
         mKeyguardStateController = keyguardStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
-        mLockPatternUtils = lockPatternUtils;
-        mFalsingManager = falsingManager;
-        mKeyguardBypassControllerLazy = keyguardBypassControllerLazy;
-        mDismissCallbackRegistry = dismissCallbackRegistry;
         mViewMediatorCallback = viewMediatorCallback;
         mCarNavigationBarController = carNavigationBarController;
+        mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
 
         registerUserSwitchedListener();
     }
@@ -147,11 +130,9 @@
 
     @Override
     public void onFinishInflate() {
-        mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
-                mViewMediatorCallback, mLockPatternUtils,
-                getLayout().findViewById(R.id.keyguard_container), mDismissCallbackRegistry,
-                mExpansionCallback, mKeyguardStateController, mFalsingManager,
-                mKeyguardBypassControllerLazy.get());
+        mBouncer = mKeyguardBouncerComponentFactory
+                .build(getLayout().findViewById(R.id.keyguard_container), mExpansionCallback)
+                .createKeyguardBouncer();
         mBiometricUnlockControllerLazy.get().setKeyguardViewController(this);
     }
 
@@ -359,9 +340,8 @@
     public void registerStatusBar(StatusBar statusBar, ViewGroup container,
             NotificationPanelViewController notificationPanelViewController,
             BiometricUnlockController biometricUnlockController,
-            DismissCallbackRegistry dismissCallbackRegistry, ViewGroup lockIconContainer,
-            View notificationContainer, KeyguardBypassController bypassController,
-            FalsingManager falsingManager) {
+            ViewGroup lockIconContainer,
+            View notificationContainer, KeyguardBypassController bypassController) {
         // no-op
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 9584850..bf8cda3 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -340,24 +340,28 @@
         mTopNavigationBarView = mCarNavigationBarController.getTopBar(isDeviceSetupForUser());
         if (mTopNavigationBarView != null) {
             mSystemBarConfigs.insetSystemBar(SystemBarConfigs.TOP, mTopNavigationBarView);
+            mSystemBarConfigs.setInsetUpdater(SystemBarConfigs.TOP, mTopNavigationBarView);
             mTopNavigationBarWindow.addView(mTopNavigationBarView);
         }
 
         mBottomNavigationBarView = mCarNavigationBarController.getBottomBar(isDeviceSetupForUser());
         if (mBottomNavigationBarView != null) {
             mSystemBarConfigs.insetSystemBar(SystemBarConfigs.BOTTOM, mBottomNavigationBarView);
+            mSystemBarConfigs.setInsetUpdater(SystemBarConfigs.BOTTOM, mBottomNavigationBarView);
             mBottomNavigationBarWindow.addView(mBottomNavigationBarView);
         }
 
         mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(isDeviceSetupForUser());
         if (mLeftNavigationBarView != null) {
             mSystemBarConfigs.insetSystemBar(SystemBarConfigs.LEFT, mLeftNavigationBarView);
+            mSystemBarConfigs.setInsetUpdater(SystemBarConfigs.LEFT, mLeftNavigationBarView);
             mLeftNavigationBarWindow.addView(mLeftNavigationBarView);
         }
 
         mRightNavigationBarView = mCarNavigationBarController.getRightBar(isDeviceSetupForUser());
         if (mRightNavigationBarView != null) {
             mSystemBarConfigs.insetSystemBar(SystemBarConfigs.RIGHT, mRightNavigationBarView);
+            mSystemBarConfigs.setInsetUpdater(SystemBarConfigs.RIGHT, mRightNavigationBarView);
             mRightNavigationBarWindow.addView(mRightNavigationBarView);
         }
     }
@@ -367,7 +371,7 @@
     }
 
     private void attachNavBarBySide(int side) {
-        switch(side) {
+        switch (side) {
             case SystemBarConfigs.TOP:
                 if (mTopNavigationBarWindow != null) {
                     mWindowManager.addView(mTopNavigationBarWindow,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
index 51a8838..529083f4b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
@@ -23,6 +23,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.systemui.car.hvac.HvacController;
+import com.android.systemui.car.statusbar.UserNameViewController;
 import com.android.systemui.dagger.SysUISingleton;
 
 import javax.inject.Inject;
@@ -38,6 +39,7 @@
     private final ButtonSelectionStateController mButtonSelectionStateController;
     private final ButtonRoleHolderController mButtonRoleHolderController;
     private final Lazy<HvacController> mHvacControllerLazy;
+    private final Lazy<UserNameViewController> mUserNameViewControllerLazy;
 
     private boolean mShowTop;
     private boolean mShowBottom;
@@ -60,12 +62,14 @@
             NavigationBarViewFactory navigationBarViewFactory,
             ButtonSelectionStateController buttonSelectionStateController,
             Lazy<HvacController> hvacControllerLazy,
+            Lazy<UserNameViewController> userNameViewControllerLazy,
             ButtonRoleHolderController buttonRoleHolderController,
             SystemBarConfigs systemBarConfigs) {
         mContext = context;
         mNavigationBarViewFactory = navigationBarViewFactory;
         mButtonSelectionStateController = buttonSelectionStateController;
         mHvacControllerLazy = hvacControllerLazy;
+        mUserNameViewControllerLazy = userNameViewControllerLazy;
         mButtonRoleHolderController = buttonRoleHolderController;
 
         // Read configuration.
@@ -109,6 +113,7 @@
         mHvacControllerLazy.get().removeAllComponents();
         mButtonSelectionStateController.removeAll();
         mButtonRoleHolderController.removeAll();
+        mUserNameViewControllerLazy.get().removeAll();
     }
 
     /** Gets the top window if configured to do so. */
@@ -218,6 +223,7 @@
         mButtonSelectionStateController.addAllButtonsWithSelectionState(view);
         mButtonRoleHolderController.addAllButtonsWithRoleName(view);
         mHvacControllerLazy.get().addTemperatureViewToController(view);
+        mUserNameViewControllerLazy.get().addUserNameView(view);
     }
 
     /** Sets a touch listener for the top navigation bar. */
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index b8bf825..1418ace 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -25,6 +25,7 @@
 import android.view.Gravity;
 import android.view.InsetsState;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -97,6 +98,7 @@
         populateMaps();
         readConfigs();
         checkEnabledBarsHaveUniqueBarTypes();
+        checkAllOverlappingBarsHaveDifferentZOrders();
         checkSystemBarEnabledForNotificationPanel();
         setInsetPaddingsForOverlappingCorners();
         sortSystemBarSidesByZOrder();
@@ -123,16 +125,60 @@
     }
 
     protected void insetSystemBar(@SystemBarSide int side, CarNavigationBarView view) {
+        if (mSystemBarConfigMap.get(side) == null) return;
+
         int[] paddings = mSystemBarConfigMap.get(side).getPaddings();
         view.setPadding(paddings[2], paddings[0], paddings[3], paddings[1]);
     }
 
+    protected void setInsetUpdater(@SystemBarSide int side, CarNavigationBarView view) {
+        view.setOnApplyWindowInsetsListener((v, insets) -> {
+            updateInsetPaddings(side, getSystemBarsVisibility(insets));
+            insetSystemBar(side, view);
+            return insets;
+        });
+    }
+
     protected List<Integer> getSystemBarSidesByZOrder() {
         return mSystemBarSidesByZOrder;
     }
 
     @VisibleForTesting
-    protected static int getHunZOrder() {
+    void updateInsetPaddings(@SystemBarSide int side,
+            Map<@SystemBarSide Integer, Boolean> barVisibilities) {
+        SystemBarConfig currentConfig = mSystemBarConfigMap.get(side);
+
+        if (currentConfig == null) return;
+
+        if (isHorizontalBar(side)) {
+            if (mLeftNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
+                    LEFT).getZOrder()) {
+                currentConfig.setPaddingBySide(LEFT,
+                        barVisibilities.get(LEFT) ? mSystemBarConfigMap.get(LEFT).getGirth() : 0);
+            }
+            if (mRightNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
+                    RIGHT).getZOrder()) {
+                currentConfig.setPaddingBySide(RIGHT,
+                        barVisibilities.get(RIGHT) ? mSystemBarConfigMap.get(RIGHT).getGirth() : 0);
+            }
+        }
+        if (isVerticalBar(side)) {
+            if (mTopNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
+                    TOP).getZOrder()) {
+                currentConfig.setPaddingBySide(TOP,
+                        barVisibilities.get(TOP) ? mSystemBarConfigMap.get(TOP).getGirth() : 0);
+            }
+            if (mBottomNavBarEnabled && currentConfig.getZOrder() < mSystemBarConfigMap.get(
+                    BOTTOM).getZOrder()) {
+                currentConfig.setPaddingBySide(BOTTOM,
+                        barVisibilities.get(BOTTOM) ? mSystemBarConfigMap.get(BOTTOM).getGirth()
+                                : 0);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    static int getHunZOrder() {
         return HUN_ZORDER;
     }
 
@@ -224,8 +270,14 @@
         }
     }
 
-    private void checkSystemBarEnabledForNotificationPanel() throws RuntimeException {
+    private void checkAllOverlappingBarsHaveDifferentZOrders() {
+        checkOverlappingBarsHaveDifferentZOrders(TOP, LEFT);
+        checkOverlappingBarsHaveDifferentZOrders(TOP, RIGHT);
+        checkOverlappingBarsHaveDifferentZOrders(BOTTOM, LEFT);
+        checkOverlappingBarsHaveDifferentZOrders(BOTTOM, RIGHT);
+    }
 
+    private void checkSystemBarEnabledForNotificationPanel() throws RuntimeException {
         String notificationPanelMediatorName =
                 mResources.getString(R.string.config_notificationPanelViewMediator);
         if (notificationPanelMediatorName == null) {
@@ -253,43 +305,12 @@
     }
 
     private void setInsetPaddingsForOverlappingCorners() {
-        setInsetPaddingForOverlappingCorner(TOP, LEFT);
-        setInsetPaddingForOverlappingCorner(TOP, RIGHT);
-        setInsetPaddingForOverlappingCorner(BOTTOM, LEFT);
-        setInsetPaddingForOverlappingCorner(BOTTOM, RIGHT);
-    }
-
-    private void setInsetPaddingForOverlappingCorner(@SystemBarSide int horizontalSide,
-            @SystemBarSide int verticalSide) {
-
-        if (isVerticalBar(horizontalSide) || isHorizontalBar(verticalSide)) {
-            Log.w(TAG, "configureBarPaddings: Returning immediately since the horizontal and "
-                    + "vertical sides were not provided correctly.");
-            return;
-        }
-
-        SystemBarConfig horizontalBarConfig = mSystemBarConfigMap.get(horizontalSide);
-        SystemBarConfig verticalBarConfig = mSystemBarConfigMap.get(verticalSide);
-
-        if (verticalBarConfig != null && horizontalBarConfig != null) {
-            int horizontalBarZOrder = horizontalBarConfig.getZOrder();
-            int horizontalBarGirth = horizontalBarConfig.getGirth();
-            int verticalBarZOrder = verticalBarConfig.getZOrder();
-            int verticalBarGirth = verticalBarConfig.getGirth();
-
-            if (horizontalBarZOrder > verticalBarZOrder) {
-                verticalBarConfig.setPaddingBySide(horizontalSide, horizontalBarGirth);
-            } else if (horizontalBarZOrder < verticalBarZOrder) {
-                horizontalBarConfig.setPaddingBySide(verticalSide, verticalBarGirth);
-            } else {
-                throw new RuntimeException(
-                        BAR_TITLE_MAP.get(horizontalSide) + " " + BAR_TITLE_MAP.get(verticalSide)
-                                + " have the same Z-Order, and so their placing order cannot be "
-                                + "determined. Determine which bar should be placed on top of the "
-                                + "other bar and change the Z-order in config.xml accordingly."
-                );
-            }
-        }
+        Map<@SystemBarSide Integer, Boolean> systemBarVisibilityOnInit =
+                getSystemBarsVisibilityOnInit();
+        updateInsetPaddings(TOP, systemBarVisibilityOnInit);
+        updateInsetPaddings(BOTTOM, systemBarVisibilityOnInit);
+        updateInsetPaddings(LEFT, systemBarVisibilityOnInit);
+        updateInsetPaddings(RIGHT, systemBarVisibilityOnInit);
     }
 
     private void sortSystemBarSidesByZOrder() {
@@ -307,6 +328,75 @@
         });
     }
 
+    @InsetsState.InternalInsetsType
+    private int getSystemBarTypeBySide(@SystemBarSide int side) {
+        return mSystemBarConfigMap.get(side) != null
+                ? mSystemBarConfigMap.get(side).getBarType() : null;
+    }
+
+    // On init, system bars are visible as long as they are enabled.
+    private Map<@SystemBarSide Integer, Boolean> getSystemBarsVisibilityOnInit() {
+        ArrayMap<@SystemBarSide Integer, Boolean> visibilityMap = new ArrayMap<>();
+        visibilityMap.put(TOP, mTopNavBarEnabled);
+        visibilityMap.put(BOTTOM, mBottomNavBarEnabled);
+        visibilityMap.put(LEFT, mLeftNavBarEnabled);
+        visibilityMap.put(RIGHT, mRightNavBarEnabled);
+        return visibilityMap;
+    }
+
+    private Map<@SystemBarSide Integer, Boolean> getSystemBarsVisibility(WindowInsets insets) {
+        ArrayMap<@SystemBarSide Integer, Boolean> visibilityMap = new ArrayMap<>();
+        if (mTopNavBarEnabled) {
+            visibilityMap.put(TOP, getSystemBarInsetVisibleBySide(TOP, insets));
+        }
+        if (mBottomNavBarEnabled) {
+            visibilityMap.put(BOTTOM, getSystemBarInsetVisibleBySide(BOTTOM, insets));
+        }
+        if (mLeftNavBarEnabled) {
+            visibilityMap.put(LEFT, getSystemBarInsetVisibleBySide(LEFT, insets));
+        }
+        if (mRightNavBarEnabled) {
+            visibilityMap.put(RIGHT, getSystemBarInsetVisibleBySide(RIGHT, insets));
+        }
+        return visibilityMap;
+    }
+
+    private boolean getSystemBarInsetVisibleBySide(@SystemBarSide int side, WindowInsets insets) {
+        if (mSystemBarConfigMap.get(side) == null) return false;
+
+        int internalInsetType = BAR_TYPE_MAP[getSystemBarTypeBySide(side)];
+        int publicInsetType = InsetsState.toPublicType(internalInsetType);
+
+        return insets.isVisible(publicInsetType);
+    }
+
+    private void checkOverlappingBarsHaveDifferentZOrders(@SystemBarSide int horizontalSide,
+            @SystemBarSide int verticalSide) {
+
+        if (isVerticalBar(horizontalSide) || isHorizontalBar(verticalSide)) {
+            Log.w(TAG, "configureBarPaddings: Returning immediately since the horizontal and "
+                    + "vertical sides were not provided correctly.");
+            return;
+        }
+
+        SystemBarConfig horizontalBarConfig = mSystemBarConfigMap.get(horizontalSide);
+        SystemBarConfig verticalBarConfig = mSystemBarConfigMap.get(verticalSide);
+
+        if (verticalBarConfig != null && horizontalBarConfig != null) {
+            int horizontalBarZOrder = horizontalBarConfig.getZOrder();
+            int verticalBarZOrder = verticalBarConfig.getZOrder();
+
+            if (horizontalBarZOrder == verticalBarZOrder) {
+                throw new RuntimeException(
+                        BAR_TITLE_MAP.get(horizontalSide) + " " + BAR_TITLE_MAP.get(verticalSide)
+                                + " have the same Z-Order, and so their placing order cannot be "
+                                + "determined. Determine which bar should be placed on top of the "
+                                + "other bar and change the Z-order in config.xml accordingly."
+                );
+            }
+        }
+    }
+
     private static boolean isHorizontalBar(@SystemBarSide int side) {
         return side == TOP || side == BOTTOM;
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 38e1a48..8d91e7e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -54,8 +54,8 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import java.util.concurrent.Executor;
 
@@ -91,7 +91,6 @@
     private RecyclerView mNotificationList;
     private NotificationViewController mNotificationViewController;
 
-    private boolean mIsTracking;
     private boolean mNotificationListAtEnd;
     private float mFirstTouchDownOnGlassPane;
     private boolean mNotificationListAtEndAtTimeOfTouch;
@@ -306,7 +305,7 @@
                 mFirstTouchDownOnGlassPane = event.getRawX();
                 mNotificationListAtEndAtTimeOfTouch = mNotificationListAtEnd;
                 // Reset the tracker when there is a touch down on the glass pane.
-                mIsTracking = false;
+                setIsTracking(false);
                 // Pass the down event to gesture detector so that it knows where the touch event
                 // started.
                 closeGestureDetector.onTouchEvent(event);
@@ -341,15 +340,15 @@
 
             // If the card is swiping we should not allow the notification shade to close.
             // Hence setting mNotificationListAtEndAtTimeOfTouch to false will stop that
-            // for us. We are also checking for mIsTracking because while swiping the
+            // for us. We are also checking for isTracking() because while swiping the
             // notification shade to close if the user goes a bit horizontal while swiping
             // upwards then also this should close.
-            if (mIsNotificationCardSwiping && !mIsTracking) {
+            if (mIsNotificationCardSwiping && !isTracking()) {
                 mNotificationListAtEndAtTimeOfTouch = false;
             }
 
             boolean handled = closeGestureDetector.onTouchEvent(event);
-            boolean isTracking = mIsTracking;
+            boolean isTracking = isTracking();
             Rect rect = getLayout().getClipBounds();
             float clippedHeight = 0;
             if (rect != null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java
new file mode 100644
index 0000000..5ef8aa1
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/UserNameViewController.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 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.car.statusbar;
+
+import android.car.Car;
+import android.car.user.CarUserManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.dagger.SysUISingleton;
+
+import javax.inject.Inject;
+
+/**
+ * Controls a TextView with the current driver's username
+ */
+@SysUISingleton
+public class UserNameViewController {
+    private static final String TAG = "UserNameViewController";
+
+    private Context mContext;
+    private UserManager mUserManager;
+    private CarUserManager mCarUserManager;
+    private CarServiceProvider mCarServiceProvider;
+    private CarDeviceProvisionedController mCarDeviceProvisionedController;
+    private BroadcastDispatcher mBroadcastDispatcher;
+    private TextView mUserNameView;
+
+    private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateUser(mCarDeviceProvisionedController.getCurrentUser());
+        }
+    };
+
+    private final CarUserManager.UserLifecycleListener mUserLifecycleListener =
+            new CarUserManager.UserLifecycleListener() {
+                @Override
+                public void onEvent(CarUserManager.UserLifecycleEvent event) {
+                    if (event.getEventType()
+                            == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
+                        updateUser(event.getUserId());
+                    }
+                }
+            };
+
+    @Inject
+    public UserNameViewController(Context context, CarServiceProvider carServiceProvider,
+            UserManager userManager, BroadcastDispatcher broadcastDispatcher,
+            CarDeviceProvisionedController carDeviceProvisionedController) {
+        mContext = context;
+        mCarServiceProvider = carServiceProvider;
+        mUserManager = userManager;
+        mBroadcastDispatcher = broadcastDispatcher;
+        mCarDeviceProvisionedController = carDeviceProvisionedController;
+    }
+
+    /**
+     * Find the {@link TextView} for the driver's user name from a view and if found set it with the
+     * current driver's user name.
+     */
+    public void addUserNameView(View v) {
+        TextView userNameView = v.findViewById(R.id.user_name_text);
+        if (userNameView != null) {
+            if (mUserNameView == null) {
+                registerForUserChangeEvents();
+            }
+            mUserNameView = userNameView;
+            updateUser(mCarDeviceProvisionedController.getCurrentUser());
+        }
+    }
+
+    /**
+     * Clean up the controller and unregister receiver.
+     */
+    public void removeAll() {
+        mBroadcastDispatcher.unregisterReceiver(mUserUpdateReceiver);
+        if (mCarUserManager != null) {
+            mCarUserManager.removeListener(mUserLifecycleListener);
+        }
+    }
+
+    private void registerForUserChangeEvents() {
+        // Register for user switching
+        mCarServiceProvider.addListener(car -> {
+            mCarUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE);
+            if (mCarUserManager != null) {
+                mCarUserManager.addListener(Runnable::run, mUserLifecycleListener);
+            } else {
+                Log.e(TAG, "CarUserManager could not be obtained.");
+            }
+        });
+        // Also register for user info changing
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+        mBroadcastDispatcher.registerReceiver(mUserUpdateReceiver, filter, /* executor= */ null,
+                UserHandle.ALL);
+    }
+
+    private void updateUser(int userId) {
+        if (mUserNameView != null) {
+            UserInfo currentUserInfo = mUserManager.getUserInfo(userId);
+            mUserNameView.setText(currentUserInfo.name);
+        }
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
index 5bd8797..023b5b4 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
@@ -131,7 +131,7 @@
     }
 
     private List<UserInfo> getUsersForUserGrid() {
-        return mUserManager.getUsers(/* excludeDying= */ true)
+        return mUserManager.getAliveUsers()
                 .stream()
                 .filter(UserInfo::supportsSwitchToByUser)
                 .collect(Collectors.toList());
@@ -338,7 +338,7 @@
                 maxSupportedUsers -= 1;
             }
 
-            List<UserInfo> users = mUserManager.getUsers(/* excludeDying= */ true);
+            List<UserInfo> users = mUserManager.getAliveUsers();
 
             // Count all users that are managed profiles of another user.
             int managedProfilesCount = 0;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
index bde31f1..d928ad0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
@@ -34,7 +34,7 @@
 import com.android.systemui.R;
 import com.android.systemui.car.CarDeviceProvisionedController;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -297,14 +297,17 @@
             float from = getCurrentStartPosition(rect);
             if (from != to) {
                 animate(from, to, velocity, isClosing);
-                return;
             }
+
+            // If we swipe down the notification panel all the way to the bottom of the screen
+            // (i.e. from == to), then we have finished animating the panel.
+            return;
         }
 
         // We will only be here if the shade is being opened programmatically or via button when
         // height of the layout was not calculated.
-        ViewTreeObserver notificationTreeObserver = getLayout().getViewTreeObserver();
-        notificationTreeObserver.addOnGlobalLayoutListener(
+        ViewTreeObserver panelTreeObserver = getLayout().getViewTreeObserver();
+        panelTreeObserver.addOnGlobalLayoutListener(
                 new ViewTreeObserver.OnGlobalLayoutListener() {
                     @Override
                     public void onGlobalLayout() {
@@ -507,6 +510,11 @@
         return mIsTracking;
     }
 
+    /** Sets whether the panel is currently tracking or not. */
+    protected final void setIsTracking(boolean isTracking) {
+        mIsTracking = isTracking;
+    }
+
     /** Returns {@code true} if the panel is currently animating. */
     protected final boolean isAnimating() {
         return mIsAnimating;
@@ -545,7 +553,7 @@
             }
             setPanelVisible(true);
 
-            // clips the view for the notification shade when the user scrolls to open.
+            // clips the view for the panel when the user scrolls to open.
             setViewClipBounds((int) event2.getRawY());
 
             // Initially the scroll starts with height being zero. This checks protects from divide
@@ -600,11 +608,11 @@
                 boolean isInClosingDirection = mAnimateDirection * distanceY > 0;
 
                 // This check is to figure out if onScroll was called while swiping the card at
-                // bottom of the list. At that time we should not allow notification shade to
+                // bottom of the panel. At that time we should not allow panel to
                 // close. We are also checking for the upwards swipe gesture here because it is
-                // possible if a user is closing the notification shade and while swiping starts
+                // possible if a user is closing the panel and while swiping starts
                 // to open again but does not fling. At that time we should allow the
-                // notification shade to close fully or else it would stuck in between.
+                // panel to close fully or else it would stuck in between.
                 if (Math.abs(getLayout().getHeight() - y)
                         > SWIPE_DOWN_MIN_DISTANCE && isInClosingDirection) {
                     setViewClipBounds((int) y);
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
index fd6685f..6d31a8d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
@@ -22,8 +22,6 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.pip.phone.PipMenuActivity;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.wm.DisplaySystemBarsController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
@@ -43,12 +41,4 @@
         return new DisplaySystemBarsController(context, wmService, displayController,
                 mainHandler, transactionPool);
     }
-
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
-    @SysUISingleton
-    @PipMenuActivityClass
-    @Provides
-    Class<?> providePipMenuActivityClass() {
-        return PipMenuActivity.class;
-    }
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
new file mode 100644
index 0000000..a3a55aa
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 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.car.hvac;
+
+import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
+
+import static com.android.systemui.car.hvac.HvacController.convertToCelsius;
+import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.car.Car;
+import android.car.VehicleUnit;
+import android.car.hardware.property.CarPropertyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+@CarSystemUiTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class AdjustableTemperatureViewTest extends SysuiTestCase {
+
+    private static final float TEMP_CELSIUS = 22.0f;
+    private final String mFormat = getContext().getString(R.string.hvac_temperature_format);
+    private AdjustableTemperatureView mAdjustableTemperatureView;
+    private HvacController mHvacController;
+
+    @Mock
+    private Car mCar;
+    @Mock
+    private CarPropertyManager mCarPropertyManager;
+    @Mock
+    private Executor mExecutor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mCar.isConnected()).thenReturn(true);
+        when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager);
+
+        CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar);
+        mHvacController = new HvacController(carServiceProvider, mExecutor);
+        mHvacController.connectToCarService();
+        mAdjustableTemperatureView = new AdjustableTemperatureView(getContext(), /* attrs= */ null);
+        mAdjustableTemperatureView.onFinishInflate();
+        mAdjustableTemperatureView.setHvacController(mHvacController);
+    }
+
+    @Test
+    public void addTemperatureViewToController_setsTemperatureView() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP_CELSIUS);
+
+        mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+        TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+        assertEquals(tempText.getText(), String.format(mFormat, TEMP_CELSIUS));
+    }
+
+    @Test
+    public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP_CELSIUS);
+        when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true);
+        when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT);
+
+        mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+        TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+        assertEquals(tempText.getText(), String.format(mFormat, convertToFahrenheit(TEMP_CELSIUS)));
+    }
+
+    @Test
+    public void adjustableViewIncreaseButton_setsTempWithCarPropertyManager() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP_CELSIUS);
+        mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+        mAdjustableTemperatureView.findViewById(R.id.hvac_increase_button).callOnClick();
+
+        ArgumentCaptor<Runnable> setTempRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mExecutor).execute(setTempRunnableCaptor.capture());
+        setTempRunnableCaptor.getValue().run();
+        verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(),
+                eq(TEMP_CELSIUS + 1));
+    }
+
+    @Test
+    public void adjustableViewDecreaseButton_setsTempWithCarPropertyManager() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP_CELSIUS);
+        mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+        mAdjustableTemperatureView.findViewById(R.id.hvac_decrease_button).callOnClick();
+
+        ArgumentCaptor<Runnable> setTempRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mExecutor).execute(setTempRunnableCaptor.capture());
+        setTempRunnableCaptor.getValue().run();
+        verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(),
+                eq(TEMP_CELSIUS - 1));
+    }
+
+    @Test
+    public void adjustableViewIncreaseButton_inFahrenheit_setsTempWithCarPropertyManager() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP_CELSIUS);
+        when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true);
+        when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT);
+        mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+        mAdjustableTemperatureView.findViewById(R.id.hvac_increase_button).callOnClick();
+
+        ArgumentCaptor<Runnable> setTempRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mExecutor).execute(setTempRunnableCaptor.capture());
+        setTempRunnableCaptor.getValue().run();
+        verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(),
+                eq(convertToCelsius(convertToFahrenheit(TEMP_CELSIUS) + 1)));
+    }
+
+    @Test
+    public void adjustableViewDecreaseButton_inFahrenheit_setsTempWithCarPropertyManager() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP_CELSIUS);
+        when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true);
+        when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT);
+        mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+        mAdjustableTemperatureView.findViewById(R.id.hvac_decrease_button).callOnClick();
+
+        ArgumentCaptor<Runnable> setTempRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mExecutor).execute(setTempRunnableCaptor.capture());
+        setTempRunnableCaptor.getValue().run();
+        verify(mCarPropertyManager).setFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt(),
+                eq(convertToCelsius(convertToFahrenheit(TEMP_CELSIUS) - 1)));
+    }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
index e179ef1..52f07df 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
@@ -16,7 +16,13 @@
 
 package com.android.systemui.car.hvac;
 
+import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -24,7 +30,8 @@
 import static org.mockito.Mockito.when;
 
 import android.car.Car;
-import android.car.hardware.hvac.CarHvacManager;
+import android.car.VehicleUnit;
+import android.car.hardware.property.CarPropertyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -33,6 +40,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.car.CarServiceProvider;
 import com.android.systemui.car.CarSystemUiTest;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,92 +55,92 @@
 @SmallTest
 public class HvacControllerTest extends SysuiTestCase {
 
-    private static final int PROPERTY_ID = 1;
     private static final int AREA_ID = 1;
-    private static final float VALUE = 72.0f;
+    private static final float TEMP = 72.0f;
 
     private HvacController mHvacController;
-    private CarServiceProvider mCarServiceProvider;
 
     @Mock
     private Car mCar;
     @Mock
-    private CarHvacManager mCarHvacManager;
+    private CarPropertyManager mCarPropertyManager;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCar.isConnected()).thenReturn(true);
-        when(mCar.getCarManager(Car.HVAC_SERVICE)).thenReturn(mCarHvacManager);
+        when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager);
 
-        mCarServiceProvider = new CarServiceProvider(mContext, mCar);
-        mHvacController = new HvacController(mCarServiceProvider);
+        CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar);
+        mHvacController = new HvacController(carServiceProvider,
+                new FakeExecutor(new FakeSystemClock()));
         mHvacController.connectToCarService();
     }
 
     @Test
     public void connectToCarService_registersCallback() {
-        verify(mCarHvacManager).registerCallback(any());
+        verify(mCarPropertyManager).registerCallback(any(), eq(HVAC_TEMPERATURE_SET), anyFloat());
+        verify(mCarPropertyManager).registerCallback(any(), eq(HVAC_TEMPERATURE_DISPLAY_UNITS),
+                anyFloat());
     }
 
     @Test
     public void addTemperatureViewToController_usingTemperatureView_registersView() {
-        TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
+        TemperatureTextView v = setupMockTemperatureTextView(AREA_ID, TEMP);
         mHvacController.addTemperatureViewToController(v);
 
-        verify(v).setTemp(VALUE);
+        verify(v).setTemp(TEMP);
     }
 
     @Test
     public void addTemperatureViewToController_usingSameTemperatureView_registersFirstView() {
-        TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
+        TemperatureTextView v = setupMockTemperatureTextView(AREA_ID, TEMP);
         mHvacController.addTemperatureViewToController(v);
-        verify(v).setTemp(VALUE);
-        resetTemperatureView(v, PROPERTY_ID, AREA_ID);
+        verify(v).setTemp(TEMP);
+        resetTemperatureView(v, AREA_ID);
 
         mHvacController.addTemperatureViewToController(v);
-        verify(v, never()).setTemp(VALUE);
+        verify(v, never()).setTemp(TEMP);
     }
 
     @Test
     public void addTemperatureViewToController_usingDifferentTemperatureView_registersBothViews() {
-        TemperatureTextView v1 = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
+        TemperatureTextView v1 = setupMockTemperatureTextView(AREA_ID, TEMP);
         mHvacController.addTemperatureViewToController(v1);
-        verify(v1).setTemp(VALUE);
+        verify(v1).setTemp(TEMP);
 
         TemperatureTextView v2 = setupMockTemperatureTextView(
-                PROPERTY_ID + 1,
                 AREA_ID + 1,
-                VALUE + 1);
+                TEMP + 1);
         mHvacController.addTemperatureViewToController(v2);
-        verify(v2).setTemp(VALUE + 1);
+        verify(v2).setTemp(TEMP + 1);
     }
 
     @Test
-    public void removeAllComponents_ableToRegisterSameView() {
-        TemperatureTextView v = setupMockTemperatureTextView(PROPERTY_ID, AREA_ID, VALUE);
-        mHvacController.addTemperatureViewToController(v);
-        verify(v).setTemp(VALUE);
-
-        mHvacController.removeAllComponents();
-        resetTemperatureView(v, PROPERTY_ID, AREA_ID);
+    public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() {
+        when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true);
+        when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT);
+        TemperatureTextView v = setupMockTemperatureTextView(AREA_ID, TEMP);
 
         mHvacController.addTemperatureViewToController(v);
-        verify(v).setTemp(VALUE);
+
+        verify(v).setDisplayInFahrenheit(true);
+        verify(v).setTemp(TEMP);
     }
 
-    private TemperatureTextView setupMockTemperatureTextView(int propertyId, int areaId,
-            float value) {
+    private TemperatureTextView setupMockTemperatureTextView(int areaId, float value) {
         TemperatureTextView v = mock(TemperatureTextView.class);
-        resetTemperatureView(v, propertyId, areaId);
-        when(mCarHvacManager.isPropertyAvailable(propertyId, areaId)).thenReturn(true);
-        when(mCarHvacManager.getFloatProperty(propertyId, areaId)).thenReturn(value);
+        resetTemperatureView(v, areaId);
+        when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, areaId)).thenReturn(
+                true);
+        when(mCarPropertyManager.getFloatProperty(HVAC_TEMPERATURE_SET, areaId)).thenReturn(value);
         return v;
     }
 
-    private void resetTemperatureView(TemperatureTextView view, int propertyId, int areaId) {
+    private void resetTemperatureView(TemperatureTextView view, int areaId) {
         reset(view);
-        when(view.getPropertyId()).thenReturn(propertyId);
         when(view.getAreaId()).thenReturn(areaId);
     }
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/TemperatureTextViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/TemperatureTextViewTest.java
new file mode 100644
index 0000000..3ed8111
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/TemperatureTextViewTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 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.car.hvac;
+
+import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
+import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
+
+import static com.android.systemui.car.hvac.HvacController.convertToFahrenheit;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.car.Car;
+import android.car.VehicleUnit;
+import android.car.hardware.property.CarPropertyManager;
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@CarSystemUiTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class TemperatureTextViewTest extends SysuiTestCase {
+    private static final float TEMP = 72.0f;
+    private final String mFormat = getContext().getString(R.string.hvac_temperature_format);
+    private HvacController mHvacController;
+    private TemperatureTextView mTextView;
+
+    @Mock
+    private Context mContext;
+
+    @Mock
+    private Car mCar;
+    @Mock
+    private CarPropertyManager mCarPropertyManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mCar.isConnected()).thenReturn(true);
+        when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager);
+
+        CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar);
+        mHvacController = new HvacController(carServiceProvider,
+                new FakeExecutor(new FakeSystemClock()));
+        mHvacController.connectToCarService();
+        mTextView = new TemperatureTextView(getContext(), /* attrs= */ null);
+    }
+
+    @Test
+    public void addTemperatureViewToController_usingTemperatureView_registersView() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP);
+
+        mHvacController.addTemperatureViewToController(mTextView);
+
+        assertEquals(mTextView.getText(), String.format(mFormat, TEMP));
+    }
+
+    @Test
+    public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() {
+        when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+                anyInt())).thenReturn(true);
+        when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+                TEMP);
+        when(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(true);
+        when(mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
+                VEHICLE_AREA_TYPE_GLOBAL)).thenReturn(VehicleUnit.FAHRENHEIT);
+
+        mHvacController.addTemperatureViewToController(mTextView);
+
+        assertEquals(mTextView.getText(), String.format(mFormat, convertToFahrenheit(TEMP)));
+    }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index 62dc236..63d4004 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -35,20 +34,17 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.car.CarServiceProvider;
 import com.android.systemui.car.CarSystemUiTest;
 import com.android.systemui.car.navigationbar.CarNavigationBarController;
 import com.android.systemui.car.window.OverlayViewGlobalStateController;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
@@ -58,31 +54,36 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import dagger.Lazy;
-
 @CarSystemUiTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class CarKeyguardViewControllerTest extends SysuiTestCase {
 
-    private TestableCarKeyguardViewController mCarKeyguardViewController;
+    private CarKeyguardViewController mCarKeyguardViewController;
 
     @Mock
     private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
     @Mock
-    private KeyguardBouncer mBouncer;
-    @Mock
-    private CarNavigationBarController mCarNavigationBarController;
-    @Mock
     private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener;
+    @Mock
+    private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
+    @Mock
+    private KeyguardBouncerComponent mKeyguardBouncerComponent;
+    @Mock
+    private KeyguardBouncer mBouncer;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mCarKeyguardViewController = new TestableCarKeyguardViewController(
-                mContext,
+        when(mKeyguardBouncerComponentFactory.build(
+                any(ViewGroup.class),
+                any(KeyguardBouncer.BouncerExpansionCallback.class)))
+                .thenReturn(mKeyguardBouncerComponent);
+        when(mKeyguardBouncerComponent.createKeyguardBouncer()).thenReturn(mBouncer);
+
+        mCarKeyguardViewController = new CarKeyguardViewController(
                 Handler.getMain(),
                 mock(CarServiceProvider.class),
                 mOverlayViewGlobalStateController,
@@ -91,10 +92,7 @@
                 () -> mock(BiometricUnlockController.class),
                 mock(ViewMediatorCallback.class),
                 mock(CarNavigationBarController.class),
-                mock(LockPatternUtils.class),
-                mock(DismissCallbackRegistry.class),
-                mock(FalsingManager.class),
-                () -> mock(KeyguardBypassController.class)
+                mKeyguardBouncerComponentFactory
         );
         mCarKeyguardViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
                 R.layout.sysui_overlay_window, /* root= */ null));
@@ -202,33 +200,4 @@
 
         verify(mBouncer).hide(/* destroyView= */ true);
     }
-
-    private class TestableCarKeyguardViewController extends CarKeyguardViewController {
-
-        TestableCarKeyguardViewController(Context context,
-                Handler mainHandler,
-                CarServiceProvider carServiceProvider,
-                OverlayViewGlobalStateController overlayViewGlobalStateController,
-                KeyguardStateController keyguardStateController,
-                KeyguardUpdateMonitor keyguardUpdateMonitor,
-                Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
-                ViewMediatorCallback viewMediatorCallback,
-                CarNavigationBarController carNavigationBarController,
-                LockPatternUtils lockPatternUtils,
-                DismissCallbackRegistry dismissCallbackRegistry,
-                FalsingManager falsingManager,
-                Lazy<KeyguardBypassController> keyguardBypassControllerLazy) {
-            super(context, mainHandler, carServiceProvider, overlayViewGlobalStateController,
-                    keyguardStateController, keyguardUpdateMonitor, biometricUnlockControllerLazy,
-                    viewMediatorCallback, carNavigationBarController, lockPatternUtils,
-                    dismissCallbackRegistry, falsingManager, keyguardBypassControllerLazy);
-        }
-
-        @Override
-        public void onFinishInflate() {
-            super.onFinishInflate();
-            setKeyguardBouncer(CarKeyguardViewControllerTest.this.mBouncer);
-        }
-    }
-
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
index 0b164a2..0b3ac2a 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.car.CarSystemUiTest;
 import com.android.systemui.car.hvac.HvacController;
+import com.android.systemui.car.statusbar.UserNameViewController;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 
@@ -48,6 +49,10 @@
 @SmallTest
 public class CarNavigationBarControllerTest extends SysuiTestCase {
 
+    private static final String TOP_NOTIFICATION_PANEL =
+            "com.android.systemui.car.notification.TopNotificationPanelViewMediator";
+    private static final String BOTTOM_NOTIFICATION_PANEL =
+            "com.android.systemui.car.notification.BottomNotificationPanelViewMediator";
     private CarNavigationBarController mCarNavigationBar;
     private NavigationBarViewFactory mNavigationBarViewFactory;
     private TestableResources mTestableResources;
@@ -58,6 +63,8 @@
     private ButtonRoleHolderController mButtonRoleHolderController;
     @Mock
     private HvacController mHvacController;
+    @Mock
+    private UserNameViewController mUserNameViewController;
 
     @Before
     public void setUp() throws Exception {
@@ -73,7 +80,7 @@
     private CarNavigationBarController createNavigationBarController() {
         return new CarNavigationBarController(mContext, mNavigationBarViewFactory,
                 mButtonSelectionStateController, () -> mHvacController,
-                mButtonRoleHolderController,
+                () -> mUserNameViewController, mButtonRoleHolderController,
                 new SystemBarConfigs(mTestableResources.getResources()));
     }
 
@@ -117,6 +124,11 @@
     @Test
     public void testGetTopWindow_topDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, false);
+        mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
+        // If Top Notification Panel is used but top navigation bar is not enabled, SystemUI is
+        // expected to crash.
+        mTestableResources.addOverride(R.string.config_notificationPanelViewMediator,
+                BOTTOM_NOTIFICATION_PANEL);
         mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getTopWindow();
@@ -148,6 +160,11 @@
     @Test
     public void testGetBottomWindow_bottomDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false);
+        mTestableResources.addOverride(R.bool.config_enableTopNavigationBar, true);
+        // If Bottom Notification Panel is used but bottom navigation bar is not enabled,
+        // SystemUI is expected to crash.
+        mTestableResources.addOverride(R.string.config_notificationPanelViewMediator,
+                TOP_NOTIFICATION_PANEL);
         mCarNavigationBar = createNavigationBarController();
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
index 8b15899..072358b 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/SystemBarConfigsTest.java
@@ -25,6 +25,7 @@
 import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.ArrayMap;
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
@@ -41,16 +42,20 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 @CarSystemUiTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class SystemBarConfigsTest extends SysuiTestCase {
+    private static final int SYSTEM_BAR_GIRTH = 100;
 
     private SystemBarConfigs mSystemBarConfigs;
     @Mock
     private Resources mResources;
+    @Mock
+    private CarNavigationBarView mCarNavigationBarView;
 
     @Before
     public void setUp() {
@@ -133,6 +138,41 @@
         assertEquals(lp.type, WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL);
     }
 
+    @Test
+    public void updateInsetPaddings_overlappingBarWithHigherZOrderDisappeared_removesInset() {
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+        CarNavigationBarView leftBar = new CarNavigationBarView(mContext, /* attrs= */ null);
+        Map<Integer, Boolean> visibilities = new ArrayMap<>();
+        visibilities.put(SystemBarConfigs.TOP, false);
+        visibilities.put(SystemBarConfigs.BOTTOM, true);
+        visibilities.put(SystemBarConfigs.LEFT, true);
+        visibilities.put(SystemBarConfigs.RIGHT, true);
+
+        mSystemBarConfigs.updateInsetPaddings(SystemBarConfigs.LEFT, visibilities);
+        mSystemBarConfigs.insetSystemBar(SystemBarConfigs.LEFT, leftBar);
+
+        assertEquals(0, leftBar.getPaddingTop());
+    }
+
+    @Test
+    public void updateInsetPaddings_overlappingBarWithHigherZOrderReappeared_addsInset() {
+        mSystemBarConfigs = new SystemBarConfigs(mResources);
+        CarNavigationBarView leftBar = new CarNavigationBarView(mContext, /* attrs= */ null);
+        Map<Integer, Boolean> visibilities = new ArrayMap<>();
+        visibilities.put(SystemBarConfigs.TOP, false);
+        visibilities.put(SystemBarConfigs.BOTTOM, true);
+        visibilities.put(SystemBarConfigs.LEFT, true);
+        visibilities.put(SystemBarConfigs.RIGHT, true);
+
+        mSystemBarConfigs.updateInsetPaddings(SystemBarConfigs.LEFT, visibilities);
+        mSystemBarConfigs.insetSystemBar(SystemBarConfigs.LEFT, leftBar);
+        visibilities.put(SystemBarConfigs.TOP, true);
+        mSystemBarConfigs.updateInsetPaddings(SystemBarConfigs.LEFT, visibilities);
+        mSystemBarConfigs.insetSystemBar(SystemBarConfigs.LEFT, leftBar);
+
+        assertEquals(SYSTEM_BAR_GIRTH, leftBar.getPaddingTop());
+    }
+
     // Set valid config where all system bars are enabled.
     private void setDefaultValidConfig() {
         when(mResources.getBoolean(R.bool.config_enableTopNavigationBar)).thenReturn(true);
@@ -141,13 +181,13 @@
         when(mResources.getBoolean(R.bool.config_enableRightNavigationBar)).thenReturn(true);
 
         when(mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height)).thenReturn(100);
+                com.android.internal.R.dimen.status_bar_height)).thenReturn(SYSTEM_BAR_GIRTH);
         when(mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.navigation_bar_height)).thenReturn(100);
+                com.android.internal.R.dimen.navigation_bar_height)).thenReturn(SYSTEM_BAR_GIRTH);
         when(mResources.getDimensionPixelSize(R.dimen.car_left_navigation_bar_width)).thenReturn(
-                100);
+                SYSTEM_BAR_GIRTH);
         when(mResources.getDimensionPixelSize(R.dimen.car_right_navigation_bar_width)).thenReturn(
-                100);
+                SYSTEM_BAR_GIRTH);
 
         when(mResources.getInteger(R.integer.config_topSystemBarType)).thenReturn(0);
         when(mResources.getInteger(R.integer.config_bottomSystemBarType)).thenReturn(1);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java
new file mode 100644
index 0000000..8f9e56e
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/statusbar/UserNameViewControllerTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 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.car.statusbar;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.car.Car;
+import android.car.user.CarUserManager;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.car.CarSystemUiTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@CarSystemUiTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class UserNameViewControllerTest extends SysuiTestCase {
+
+    private final UserInfo mUserInfo1 = new UserInfo(/* id= */ 0, "Test User Name", /* flags= */ 0);
+    private final UserInfo mUserInfo2 = new UserInfo(/* id= */ 1, "Another User", /* flags= */ 0);
+    private TextView mTextView;
+    private UserNameViewController mUserNameViewController;
+
+    @Mock
+    private Car mCar;
+    @Mock
+    private CarUserManager mCarUserManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private CarDeviceProvisionedController mCarDeviceProvisionedController;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mUserManager.getUserInfo(mUserInfo1.id)).thenReturn(mUserInfo1);
+        when(mUserManager.getUserInfo(mUserInfo2.id)).thenReturn(mUserInfo2);
+        when(mCar.isConnected()).thenReturn(true);
+        when(mCar.getCarManager(Car.CAR_USER_SERVICE)).thenReturn(mCarUserManager);
+
+        CarServiceProvider carServiceProvider = new CarServiceProvider(mContext, mCar);
+        mUserNameViewController = new UserNameViewController(getContext(), carServiceProvider,
+                mUserManager, mBroadcastDispatcher, mCarDeviceProvisionedController);
+
+        mTextView = new TextView(getContext());
+        mTextView.setId(R.id.user_name_text);
+    }
+
+    @Test
+    public void addUserNameViewToController_updatesUserNameView() {
+        when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo1.id);
+
+        mUserNameViewController.addUserNameView(mTextView);
+
+        assertEquals(mTextView.getText(), mUserInfo1.name);
+    }
+
+    @Test
+    public void addUserNameViewToController_withNoTextView_doesNotUpdate() {
+        View nullView = new View(getContext());
+        mUserNameViewController.addUserNameView(nullView);
+
+        assertEquals(mTextView.getText(), "");
+        verifyZeroInteractions(mCarDeviceProvisionedController);
+        verifyZeroInteractions(mCarUserManager);
+        verifyZeroInteractions(mUserManager);
+    }
+
+    @Test
+    public void userLifecycleListener_onUserSwitchLifecycleEvent_updatesUserNameView() {
+        ArgumentCaptor<CarUserManager.UserLifecycleListener> userLifecycleListenerArgumentCaptor =
+                ArgumentCaptor.forClass(CarUserManager.UserLifecycleListener.class);
+        when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo1.id);
+        // Add the initial TextView, which registers the UserLifecycleListener
+        mUserNameViewController.addUserNameView(mTextView);
+        assertEquals(mTextView.getText(), mUserInfo1.name);
+        verify(mCarUserManager).addListener(any(), userLifecycleListenerArgumentCaptor.capture());
+
+        CarUserManager.UserLifecycleEvent event = new CarUserManager.UserLifecycleEvent(
+                CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, /* from= */ mUserInfo1.id,
+                /* to= */ mUserInfo2.id);
+        userLifecycleListenerArgumentCaptor.getValue().onEvent(event);
+
+        assertEquals(mTextView.getText(), mUserInfo2.name);
+    }
+
+    @Test
+    public void userInfoChangedBroadcast_withoutInitializingUserNameView_doesNothing() {
+        getContext().sendBroadcast(new Intent(Intent.ACTION_USER_INFO_CHANGED));
+
+        assertEquals(mTextView.getText(), "");
+        verifyZeroInteractions(mCarDeviceProvisionedController);
+    }
+
+    @Test
+    public void userInfoChangedBroadcast_withUserNameViewInitialized_updatesUserNameView() {
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverArgumentCaptor = ArgumentCaptor.forClass(
+                BroadcastReceiver.class);
+        when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo1.id);
+        mUserNameViewController.addUserNameView(mTextView);
+        assertEquals(mTextView.getText(), mUserInfo1.name);
+        verify(mBroadcastDispatcher).registerReceiver(broadcastReceiverArgumentCaptor.capture(),
+                any(), any(), any());
+
+        reset(mCarDeviceProvisionedController);
+        when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(mUserInfo2.id);
+        broadcastReceiverArgumentCaptor.getValue().onReceive(getContext(),
+                new Intent(Intent.ACTION_USER_INFO_CHANGED));
+
+        assertEquals(mTextView.getText(), mUserInfo2.name);
+        verify(mCarDeviceProvisionedController).getCurrentUser();
+    }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
index 7311cdb..bed803e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
@@ -40,8 +40,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.car.CarDeviceProvisionedController;
 import com.android.systemui.car.CarSystemUiTest;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.tests.R;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/packages/CompanionDeviceManager/TEST_MAPPING b/packages/CompanionDeviceManager/TEST_MAPPING
new file mode 100644
index 0000000..63f54fa
--- /dev/null
+++ b/packages/CompanionDeviceManager/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "include-filter": "android.os.cts.CompanionDeviceManagerTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index f8952ac..4d31ce9 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -334,6 +334,11 @@
             throw new IOException(
                     "Failed to start installation with requested size: " + mUserdataSize);
         }
+        // Reset installation session and verify that installation completes successfully.
+        mInstallationSession = null;
+        if (!mDynSystem.closePartition()) {
+            throw new IOException("Failed to complete partition installation: userdata");
+        }
     }
 
     private void installImages()
@@ -503,6 +508,12 @@
                 imageValidationThrowOrWarning(new KeyRevokedException(publicKey));
             }
         }
+
+        // Reset installation session and verify that installation completes successfully.
+        mInstallationSession = null;
+        if (!mDynSystem.closePartition()) {
+            throw new IOException("Failed to complete partition installation: " + partitionName);
+        }
     }
 
     private static String toHexString(byte[] bytes) {
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index f42bf19..11d1b0a 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -483,6 +483,13 @@
     }
 
     @Override
+    protected void onDocIdDeleted(String docId) {
+        Uri uri = DocumentsContract.buildDocumentUri(AUTHORITY, docId);
+        getContext().revokeUriPermission(uri, ~0);
+    }
+
+
+    @Override
     public Cursor queryRoots(String[] projection) throws FileNotFoundException {
         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
         synchronized (mRootsLock) {
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index 87f89ce..c840374 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -57,7 +57,7 @@
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"هل تريد إزالة هذا التطبيق "<b>"لكل"</b>" المستخدمين؟ ستتم إزالة التطبيق وبياناته من "<b>"كل"</b>" المستخدمين على هذا الجهاز."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string>
     <string name="uninstall_update_text" msgid="863648314632448705">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."</string>
-    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات. وسيؤثر هذا في جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string>
+    <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"هل تريد إعادة ضبط هذا التطبيق على الإعدادات الأصلية؟ سؤدي ذلك إلى إزالة جميع البيانات، كما سيؤثر على جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."</string>
     <string name="uninstall_keep_data" msgid="7002379587465487550">"الاحتفاظ بالحجم <xliff:g id="SIZE">%1$s</xliff:g> من بيانات التطبيق."</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"عمليات إلغاء التثبيت الجارية"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"عمليات إلغاء التثبيت غير الناجحة"</string>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml
new file mode 100644
index 0000000..16e9190
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml
@@ -0,0 +1,31 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="25.50"
+    android:viewportHeight="25.50">
+    <group
+        android:translateX="0.77"
+        android:translateY="0.23" >
+        <path
+            android:pathData="M14,12h6.54l3.12,-3.89c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3C6.44,3 2.33,5.36 0.56,6.57C0.05,6.92 -0.05,7.63 0.33,8.11L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L14,20.13V12z"
+            android:fillColor="#FFFFFF"/>
+        <path
+            android:pathData="M22.71,15.67l-1.83,1.83l1.83,1.83c0.38,0.38 0.38,1 0,1.38v0c-0.38,0.38 -1,0.39 -1.38,0l-1.83,-1.83l-1.83,1.83c-0.38,0.38 -1,0.38 -1.38,0l-0.01,-0.01c-0.38,-0.38 -0.38,-1 0,-1.38l1.83,-1.83l-1.82,-1.82c-0.38,-0.38 -0.38,-1 0,-1.38l0.01,-0.01c0.38,-0.38 1,-0.38 1.38,0l1.82,1.82l1.82,-1.82c0.38,-0.38 1,-0.38 1.38,0l0,0C23.09,14.67 23.09,15.29 22.71,15.67z"
+            android:fillColor="#FFFFFF"/>
+    </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml
new file mode 100644
index 0000000..4c338c9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2.01C7.25,2.01 2.97,4.09 0,7.4L7.582,16.625C7.582,16.627 7.58,16.629 7.58,16.631L11.99,22L12,22L13,20.789L13,17.641L13,13.119C12.68,13.039 12.34,13 12,13C10.601,13 9.351,13.64 8.531,14.639L2.699,7.539C5.269,5.279 8.58,4.01 12,4.01C15.42,4.01 18.731,5.279 21.301,7.539L16.811,13L19.4,13L24,7.4C21.03,4.09 16.75,2.01 12,2.01z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml
new file mode 100644
index 0000000..79037db
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C7.25,2 2.97,4.081 0,7.391L12,22L13,20.779L13,17.631L13,13L16.801,13L18,13L19.391,13L24,7.391C21.03,4.081 16.75,2 12,2zM12,4C14.747,4 17.423,4.819 19.701,6.313C20.259,6.678 20.795,7.085 21.301,7.529L17.389,12.287C16.029,10.868 14.119,9.99 12,9.99C9.88,9.99 7.969,10.869 6.609,12.289L2.699,7.529C5.269,5.269 8.58,4 12,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml
new file mode 100644
index 0000000..21ad128
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C7.25,2 2.97,4.081 0,7.391L3.301,11.41L12,22L13,20.779L13,17.631L13,13L16.801,13L19.391,13L20.699,11.41C20.699,11.409 20.698,11.409 20.697,11.408L24,7.391C21.03,4.081 16.75,2 12,2zM12,4C15.42,4 18.731,5.269 21.301,7.529L19.35,9.9C17.43,8.1 14.86,6.99 12,6.99C9.14,6.99 6.57,8.1 4.65,9.9C4.65,9.901 4.649,9.902 4.648,9.902L2.699,7.529C5.269,5.269 8.58,4 12,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml
new file mode 100644
index 0000000..2ec5ba3
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C7.25,2 2.97,4.08 0,7.39L12,22l1,-1.22V13h6.39L24,7.39C21.03,4.08 16.75,2 12,2z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index dcdadbd..4a11aa7 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -400,7 +400,7 @@
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktibo. Aldatzeko, sakatu hau."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Egonean moduko aplikazioaren egoera: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Abian diren zerbitzuak"</string>
-    <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ikusi eta kontrolatu unean abian diren zerbitzuak"</string>
+    <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ikusi eta kontrolatu une honetan abian diren zerbitzuak"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView inplementazioa"</string>
     <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"Ezarri WebView inplementazioa"</string>
     <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"Jada ez dago erabilgarri aukera hori. Saiatu berriro."</string>
diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml
index 743017c..7a4e71b 100644
--- a/packages/SettingsLib/res/values-ja/arrays.xml
+++ b/packages/SettingsLib/res/values-ja/arrays.xml
@@ -243,7 +243,7 @@
   </string-array>
   <string-array name="track_frame_time_entries">
     <item msgid="634406443901014984">"OFF"</item>
-    <item msgid="1288760936356000927">"バーとして画面に表示"</item>
+    <item msgid="1288760936356000927">"棒グラフとして画面に表示"</item>
     <item msgid="5023908510820531131">"<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g> 内"</item>
   </string-array>
   <string-array name="debug_hw_overdraw_entries">
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index c4b5f7e..419c18f 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -396,8 +396,8 @@
     <item msgid="1282170165150762976">"Санарип мазмун үчүн оптималдаштырылган түстөр"</item>
   </string-array>
     <string name="inactive_apps_title" msgid="5372523625297212320">"Көшүү режиминдеги колдонмолор"</string>
-    <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Которуштуруу үчүн таптап коюңуз."</string>
-    <string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Которуштуруу үчүн таптап коюңуз."</string>
+    <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Күйгүзүү үчүн басып коюңуз."</string>
+    <string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Өчүрүү үчүн басып коюңуз."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Көшүү режиминдеги колдонмонун абалы:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Иштеп жаткан кызматтар"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Учурда иштеп жаткан кызматтарды көрүп, көзөмөлдөп турасыз"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index fb7b634..58d4af1 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -204,10 +204,10 @@
     <string name="tethering_settings_not_available" msgid="266821736434699780">"Поставките за спојување не се достапни за овој корисник"</string>
     <string name="apn_settings_not_available" msgid="1147111671403342300">"Поставките за името на пристапната точка не се достапни за овој корисник"</string>
     <string name="enable_adb" msgid="8072776357237289039">"Отстранување грешки на USB"</string>
-    <string name="enable_adb_summary" msgid="3711526030096574316">"Режим на отстранување грешки кога е поврзано USB"</string>
+    <string name="enable_adb_summary" msgid="3711526030096574316">"Режим за отстранување грешки кога е поврзано USB"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"Отповикај овластувања за отстранување грешки од USB"</string>
     <string name="enable_adb_wireless" msgid="6973226350963971018">"Безжично отстранување грешки"</string>
-    <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Режим на отстранување грешки кога е поврзано Wi‑Fi"</string>
+    <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Режим за отстранување грешки кога е поврзано Wi‑Fi"</string>
     <string name="adb_wireless_error" msgid="721958772149779856">"Грешка"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Безжично отстранување грешки"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"За да ги гледате и користите достапните уреди, вклучете го безжичното отстранување грешки"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index a43412e..2fd46d9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -13,6 +13,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -29,6 +30,10 @@
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 
+import androidx.annotation.NonNull;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.UserIcons;
 import com.android.launcher3.icons.IconFactory;
@@ -49,11 +54,19 @@
     private static String sSharedSystemSharedLibPackageName;
 
     static final int[] WIFI_PIE = {
-            com.android.internal.R.drawable.ic_wifi_signal_0,
-            com.android.internal.R.drawable.ic_wifi_signal_1,
-            com.android.internal.R.drawable.ic_wifi_signal_2,
-            com.android.internal.R.drawable.ic_wifi_signal_3,
-            com.android.internal.R.drawable.ic_wifi_signal_4
+        com.android.internal.R.drawable.ic_wifi_signal_0,
+        com.android.internal.R.drawable.ic_wifi_signal_1,
+        com.android.internal.R.drawable.ic_wifi_signal_2,
+        com.android.internal.R.drawable.ic_wifi_signal_3,
+        com.android.internal.R.drawable.ic_wifi_signal_4
+    };
+
+    static final int[] SHOW_X_WIFI_PIE = {
+        R.drawable.ic_show_x_wifi_signal_0,
+        R.drawable.ic_show_x_wifi_signal_1,
+        R.drawable.ic_show_x_wifi_signal_2,
+        R.drawable.ic_show_x_wifi_signal_3,
+        R.drawable.ic_show_x_wifi_signal_4
     };
 
     public static void updateLocationEnabled(Context context, boolean enabled, int userId,
@@ -353,10 +366,22 @@
      * @throws IllegalArgumentException if an invalid RSSI level is given.
      */
     public static int getWifiIconResource(int level) {
+        return getWifiIconResource(false /* showX */, level);
+    }
+
+    /**
+     * Returns the Wifi icon resource for a given RSSI level.
+     *
+     * @param showX True if a connected Wi-Fi network has the problem which should show Pie+x
+     *              signal icon to users.
+     * @param level The number of bars to show (0-4)
+     * @throws IllegalArgumentException if an invalid RSSI level is given.
+     */
+    public static int getWifiIconResource(boolean showX, int level) {
         if (level < 0 || level >= WIFI_PIE.length) {
             throw new IllegalArgumentException("No Wifi icon found for level: " + level);
         }
-        return WIFI_PIE[level];
+        return showX ? SHOW_X_WIFI_PIE[level] : WIFI_PIE[level];
     }
 
     public static int getDefaultStorageManagerDaysToRetain(Resources resources) {
@@ -484,4 +509,25 @@
                 == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
         return !isInIwlan;
     }
+
+    /**
+     * Returns a bitmap with rounded corner.
+     *
+     * @param context application context.
+     * @param source bitmap to apply round corner.
+     * @param cornerRadius corner radius value.
+     */
+    public static Bitmap convertCornerRadiusBitmap(@NonNull Context context,
+            @NonNull Bitmap source, @NonNull float cornerRadius) {
+        final Bitmap roundedBitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(),
+                Bitmap.Config.ARGB_8888);
+        final RoundedBitmapDrawable drawable =
+                RoundedBitmapDrawableFactory.create(context.getResources(), source);
+        drawable.setAntiAlias(true);
+        drawable.setCornerRadius(cornerRadius);
+        final Canvas canvas = new Canvas(roundedBitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+        return roundedBitmap;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 68f7289..8f8f859 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -18,6 +18,7 @@
 import android.util.Pair;
 
 import androidx.annotation.DrawableRes;
+import androidx.core.graphics.drawable.IconCompat;
 
 import com.android.settingslib.R;
 import com.android.settingslib.widget.AdaptiveIcon;
@@ -216,6 +217,23 @@
     }
 
     /**
+     * Create an Icon pointing to a drawable.
+     */
+    public static IconCompat createIconWithDrawable(Drawable drawable) {
+        Bitmap bitmap;
+        if (drawable instanceof BitmapDrawable) {
+            bitmap = ((BitmapDrawable) drawable).getBitmap();
+        } else {
+            final int width = drawable.getIntrinsicWidth();
+            final int height = drawable.getIntrinsicHeight();
+            bitmap = createBitmap(drawable,
+                    width > 0 ? width : 1,
+                    height > 0 ? height : 1);
+        }
+        return IconCompat.createWithBitmap(bitmap);
+    }
+
+    /**
      * Build device icon with advanced outline
      */
     public static Drawable buildAdvancedDrawable(Context context, Drawable drawable) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index 2821af9..0d54d7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -60,8 +60,30 @@
             "com.android.settings.panel.action.MEDIA_OUTPUT_GROUP";
 
     /**
-     * An string extra specifying a media package name.
+     * A string extra specifying a media package name.
      */
     public static final String EXTRA_PACKAGE_NAME =
             "com.android.settings.panel.extra.PACKAGE_NAME";
+
+    /**
+     * An intent action to launch media output dialog.
+     */
+    public static final String ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG =
+            "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
+
+    /**
+     * Settings package name.
+     */
+    public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
+
+    /**
+     * An intent action to launch Bluetooth paring page.
+     */
+    public static final String ACTION_LAUNCH_BLUETOOTH_PAIRING =
+            "com.android.settings.action.LAUNCH_BLUETOOTH_PAIRING";
+
+    /**
+     * SystemUi package name.
+     */
+    public static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index c5e66be..a81a05f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -116,6 +116,16 @@
      */
     public static final int HIGHER_FREQ_5GHZ = 5900;
 
+    /**
+     * Lower bound on the 60 GHz (802.11ad) WIGIG channels
+     */
+    public static final int LOWER_FREQ_60GHZ = 58320;
+
+    /**
+     * Upper bound on the 60 GHz (802.11ad) WIGIG channels
+     */
+    public static final int HIGHER_FREQ_60GHZ = 70200;
+
     /** The key which identifies this AccessPoint grouping. */
     private String mKey;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index a53bc9f..bba69f2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -35,6 +35,7 @@
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
 import com.android.wifitrackerlib.WifiEntry;
+import com.android.wifitrackerlib.WifiEntry.ConnectedInfo;
 
 /**
  * Preference to display a WifiEntry in a wifi picker.
@@ -64,6 +65,7 @@
     private final IconInjector mIconInjector;
     private WifiEntry mWifiEntry;
     private int mLevel = -1;
+    private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true.
     private CharSequence mContentDescription;
     private OnButtonClickListener mOnButtonClickListener;
 
@@ -136,9 +138,15 @@
     public void refresh() {
         setTitle(mWifiEntry.getTitle());
         final int level = mWifiEntry.getLevel();
-        if (level != mLevel) {
+        final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo();
+        boolean showX = false;
+        if (connectedInfo != null) {
+            showX = !connectedInfo.isDefaultNetwork || !connectedInfo.isValidated;
+        }
+        if (level != mLevel || showX != mShowX) {
             mLevel = level;
-            updateIcon(mLevel);
+            mShowX = showX;
+            updateIcon(mShowX, mLevel);
             notifyChanged();
         }
 
@@ -184,13 +192,13 @@
     }
 
 
-    private void updateIcon(int level) {
+    private void updateIcon(boolean showX, int level) {
         if (level == -1) {
             setIcon(null);
             return;
         }
 
-        final Drawable drawable = mIconInjector.getIcon(level);
+        final Drawable drawable = mIconInjector.getIcon(showX, level);
         if (drawable != null) {
             drawable.setTintList(Utils.getColorAttr(getContext(),
                     android.R.attr.colorControlNormal));
@@ -260,8 +268,8 @@
             mContext = context;
         }
 
-        public Drawable getIcon(int level) {
-            return mContext.getDrawable(Utils.getWifiIconResource(level));
+        public Drawable getIcon(boolean showX, int level) {
+            return mContext.getDrawable(Utils.getWifiIconResource(showX, level));
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index bc58bfc..c57d4ad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -13,6 +13,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
 import android.content.Context;
 import android.content.Intent;
@@ -250,6 +251,10 @@
                     statusLabel = mContext.getString(R.string.wifi_status_no_internet);
                 }
                 return;
+            } else if (!isDefaultNetwork && mDefaultNetworkCapabilities != null
+                    && mDefaultNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+                statusLabel = mContext.getString(R.string.wifi_connected_low_quality);
+                return;
             }
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 1ace0b4..15b146d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -93,6 +93,7 @@
         StringBuilder visibility = new StringBuilder();
         StringBuilder scans24GHz = new StringBuilder();
         StringBuilder scans5GHz = new StringBuilder();
+        StringBuilder scans60GHz = new StringBuilder();
         String bssid = null;
 
         if (accessPoint.isActive() && info != null) {
@@ -115,9 +116,11 @@
 
         int maxRssi5 = INVALID_RSSI;
         int maxRssi24 = INVALID_RSSI;
+        int maxRssi60 = INVALID_RSSI;
         final int maxDisplayedScans = 4;
         int num5 = 0; // number of scanned BSSID on 5GHz band
         int num24 = 0; // number of scanned BSSID on 2.4Ghz band
+        int num60 = 0; // number of scanned BSSID on 60Ghz band
         int numBlockListed = 0;
 
         // TODO: sort list by RSSI or age
@@ -152,6 +155,19 @@
                             verboseScanResultSummary(accessPoint, result, bssid,
                                     nowMs));
                 }
+            } else if (result.frequency >= AccessPoint.LOWER_FREQ_60GHZ
+                    && result.frequency <= AccessPoint.HIGHER_FREQ_60GHZ) {
+                // Strictly speaking: [60000, 61000]
+                num60++;
+
+                if (result.level > maxRssi60) {
+                    maxRssi60 = result.level;
+                }
+                if (num60 <= maxDisplayedScans) {
+                    scans60GHz.append(
+                            verboseScanResultSummary(accessPoint, result, bssid,
+                                    nowMs));
+                }
             }
         }
         visibility.append(" [");
@@ -170,6 +186,14 @@
             }
             visibility.append(scans5GHz.toString());
         }
+        visibility.append(";");
+        if (num60 > 0) {
+            visibility.append("(").append(num60).append(")");
+            if (num60 > maxDisplayedScans) {
+                visibility.append("max=").append(maxRssi60).append(",");
+            }
+            visibility.append(scans60GHz.toString());
+        }
         if (numBlockListed > 0) {
             visibility.append("!").append(numBlockListed);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
index 46e699d..40af7dc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -17,6 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -29,6 +30,7 @@
 
 import com.android.settingslib.R;
 import com.android.wifitrackerlib.WifiEntry;
+import com.android.wifitrackerlib.WifiEntry.ConnectedInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,6 +64,17 @@
     @Mock
     private Drawable mMockDrawable4;
 
+    @Mock
+    private Drawable mMockShowXDrawable0;
+    @Mock
+    private Drawable mMockShowXDrawable1;
+    @Mock
+    private Drawable mMockShowXDrawable2;
+    @Mock
+    private Drawable mMockShowXDrawable3;
+    @Mock
+    private Drawable mMockShowXDrawable4;
+
     private static final String MOCK_TITLE = "title";
     private static final String MOCK_SUMMARY = "summary";
     private static final String FAKE_URI_STRING = "fakeuri";
@@ -75,11 +88,22 @@
         when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
         when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY);
 
-        when(mMockIconInjector.getIcon(0)).thenReturn(mMockDrawable0);
-        when(mMockIconInjector.getIcon(1)).thenReturn(mMockDrawable1);
-        when(mMockIconInjector.getIcon(2)).thenReturn(mMockDrawable2);
-        when(mMockIconInjector.getIcon(3)).thenReturn(mMockDrawable3);
-        when(mMockIconInjector.getIcon(4)).thenReturn(mMockDrawable4);
+        when(mMockIconInjector.getIcon(false /* showX */, 0)).thenReturn(mMockDrawable0);
+        when(mMockIconInjector.getIcon(false /* showX */, 1)).thenReturn(mMockDrawable1);
+        when(mMockIconInjector.getIcon(false /* showX */, 2)).thenReturn(mMockDrawable2);
+        when(mMockIconInjector.getIcon(false /* showX */, 3)).thenReturn(mMockDrawable3);
+        when(mMockIconInjector.getIcon(false /* showX */, 4)).thenReturn(mMockDrawable4);
+
+        when(mMockIconInjector.getIcon(true /* showX */, 0))
+                .thenReturn(mMockShowXDrawable0);
+        when(mMockIconInjector.getIcon(true /* showX */, 1))
+                .thenReturn(mMockShowXDrawable1);
+        when(mMockIconInjector.getIcon(true /* showX */, 2))
+                .thenReturn(mMockShowXDrawable2);
+        when(mMockIconInjector.getIcon(true /* showX */, 3))
+                .thenReturn(mMockShowXDrawable3);
+        when(mMockIconInjector.getIcon(true /* showX */, 4))
+                .thenReturn(mMockShowXDrawable4);
     }
 
     @Test
@@ -155,6 +179,70 @@
     }
 
     @Test
+    public void levelChanged_notDefaultWifiRefresh_shouldUpdateLevelIcon() {
+        final List<Drawable> iconList = new ArrayList<>();
+        final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class);
+        mockConnectedInfo.isDefaultNetwork = false;
+        when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo);
+        final WifiEntryPreference pref =
+                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+
+        when(mMockWifiEntry.getLevel()).thenReturn(0);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(2);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(3);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(4);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(-1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+
+        assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
+                mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
+    }
+
+    @Test
+    public void levelChanged_notValidatedWifiRefresh_shouldUpdateLevelIcon() {
+        final List<Drawable> iconList = new ArrayList<>();
+        final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class);
+        mockConnectedInfo.isValidated = false;
+        when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo);
+        final WifiEntryPreference pref =
+                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+
+        when(mMockWifiEntry.getLevel()).thenReturn(0);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(2);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(3);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(4);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(-1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+
+        assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
+                mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
+    }
+
+    @Test
     public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
         when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
         final WifiEntryPreference pref =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a293148..fbd8876 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -58,10 +58,14 @@
                 ConfigSettingsProto.CONNECTIVITY_SETTINGS);
         namespaceToFieldMap.put(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ConfigSettingsProto.CONTENT_CAPTURE_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_DEVICE_IDLE,
+                ConfigSettingsProto.DEVICE_IDLE_SETTINGS);
         namespaceToFieldMap.put(DeviceConfig.NAMESPACE_GAME_DRIVER,
                 ConfigSettingsProto.GAME_DRIVER_SETTINGS);
         namespaceToFieldMap.put(DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
                 ConfigSettingsProto.INPUT_NATIVE_BOOT_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                ConfigSettingsProto.JOB_SCHEDULER_SETTINGS);
         namespaceToFieldMap.put(DeviceConfig.NAMESPACE_NETD_NATIVE,
                 ConfigSettingsProto.NETD_NATIVE_SETTINGS);
         namespaceToFieldMap.put(DeviceConfig.NAMESPACE_PRIVACY,
@@ -564,9 +568,6 @@
                 Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
                 GlobalSettingsProto.Device.PROVISIONING_MOBILE_DATA_ENABLED);
         dumpSetting(s, p,
-                Settings.Global.DEVICE_IDLE_CONSTANTS,
-                GlobalSettingsProto.Device.IDLE_CONSTANTS);
-        dumpSetting(s, p,
                 Settings.Global.DEVICE_POLICY_CONSTANTS,
                 GlobalSettingsProto.Device.POLICY_CONSTANTS);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 807fbed..9c92b46 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2576,7 +2576,7 @@
         public void syncSsaidTableOnStart() {
             synchronized (mLock) {
                 // Verify that each user's packages and ssaid's are in sync.
-                for (UserInfo user : mUserManager.getUsers(true)) {
+                for (UserInfo user : mUserManager.getAliveUsers()) {
                     // Get all uids for the user's packages.
                     final List<PackageInfo> packages;
                     try {
@@ -3007,7 +3007,7 @@
 
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    List<UserInfo> users = mUserManager.getUsers(true);
+                    List<UserInfo> users = mUserManager.getAliveUsers();
 
                     final int userCount = users.size();
                     for (int i = 0; i < userCount; i++) {
@@ -3244,7 +3244,7 @@
             // is a singleton generation entry for the global settings which
             // is already incremented be the caller.
             final Uri uri = getNotificationUriFor(key, name);
-            final List<UserInfo> users = mUserManager.getUsers(/*excludeDying*/ true);
+            final List<UserInfo> users = mUserManager.getAliveUsers();
             for (int i = 0; i < users.size(); i++) {
                 final int userId = users.get(i).id;
                 if (mUserManager.isUserRunning(UserHandle.of(userId))) {
@@ -3255,7 +3255,7 @@
         }
 
         private void notifyLocationChangeForRunningUsers() {
-            final List<UserInfo> users = mUserManager.getUsers(/*excludeDying=*/ true);
+            final List<UserInfo> users = mUserManager.getAliveUsers();
 
             for (int i = 0; i < users.size(); i++) {
                 final int userId = users.get(i).id;
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index f219aec..d8f772d 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -230,7 +230,6 @@
                     Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
                     Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
                     Settings.Global.DEVICE_DEMO_MODE,
-                    Settings.Global.DEVICE_IDLE_CONSTANTS,
                     Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS,
                     Settings.Global.BATTERY_SAVER_CONSTANTS,
                     Settings.Global.BATTERY_TIP_CONSTANTS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 319b44c..190015c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -217,6 +217,9 @@
     <!-- Permission needed for CTS test - UnsupportedErrorDialogTests -->
     <uses-permission android:name="android.permission.RESET_APP_ERRORS" />
 
+    <!-- Permission needed for CTS test - CtsSystemUiTestCases:PipNotificationTests -->
+    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
+
     <!-- Permission needed to run keyguard manager tests in CTS -->
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" />
 
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
index 1c680bb..176035f 100644
--- a/packages/SimAppDialog/Android.bp
+++ b/packages/SimAppDialog/Android.bp
@@ -7,8 +7,7 @@
 
     static_libs: [
         "androidx.legacy_legacy-support-v4",
-        "setupcompat",
-        "setupdesign",
+        "setup-wizard-lib",
     ],
 
     resource_dirs: ["res"],
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
index e7368f3..873f6c5 100644
--- a/packages/SimAppDialog/AndroidManifest.xml
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -23,7 +23,7 @@
             android:name=".InstallCarrierAppActivity"
             android:exported="true"
             android:permission="android.permission.NETWORK_SETTINGS"
-            android:theme="@style/SudThemeGlif.Light">
+            android:theme="@style/SuwThemeGlif.Light">
         </activity>
     </application>
 </manifest>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 68113db..12f9bb6 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -14,17 +14,18 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.google.android.setupdesign.GlifLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:icon="@drawable/ic_signal_cellular_alt_rounded"
-    app:sucHeaderText="@string/install_carrier_app_title">
+    app:suwHeaderText="@string/install_carrier_app_title"
+    app:suwFooter="@layout/install_carrier_app_footer">
 
     <LinearLayout
-        style="@style/SudContentFrame"
+        style="@style/SuwContentFrame"
         android:id="@+id/content_frame"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -32,12 +33,12 @@
 
         <TextView
             android:id="@+id/install_carrier_app_description"
-            style="@style/SudDescription.Glif"
+            style="@style/SuwDescription.Glif"
             android:text="@string/install_carrier_app_description_default"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
 
-        <com.google.android.setupdesign.view.FillContentLayout
+        <com.android.setupwizardlib.view.FillContentLayout
             android:id="@+id/illo_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -46,12 +47,12 @@
 
             <ImageView
                 android:src="@drawable/illo_sim_app_dialog"
-                style="@style/SudContentIllustration"
+                style="@style/SuwContentIllustration"
                 android:contentDescription="@string/install_carrier_app_image_content_description"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"/>
 
-        </com.google.android.setupdesign.view.FillContentLayout>
-    </LinearLayout>
+        </com.android.setupwizardlib.view.FillContentLayout>
+</LinearLayout>
 
-</com.google.android.setupdesign.GlifLayout>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
new file mode 100644
index 0000000..10dcb77
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<com.android.setupwizardlib.view.ButtonBarLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/footer"
+    style="@style/SuwGlifButtonBar.Stackable"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Button
+        android:id="@+id/skip_button"
+        style="@style/SuwGlifButton.Secondary"
+        android:text="@string/install_carrier_app_defer_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/download_button"
+        style="@style/SuwGlifButton.Primary"
+        android:text="@string/install_carrier_app_download_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/styles.xml b/packages/SimAppDialog/res/values/styles.xml
deleted file mode 100644
index 824e380..0000000
--- a/packages/SimAppDialog/res/values/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2020 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.
--->
-<resources>
-
-  <style name="SetupWizardPartnerResource">
-    <!-- Disable to use partner overlay theme for outside setupwizard flow. -->
-    <item name="sucUsePartnerResource">false</item>
-    <!-- Enable heavy theme style inside setupwizard flow. -->
-    <item name="sudUsePartnerHeavyTheme">true</item>
-  </style>
-
-</resources>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index 0b6f9bb..abe82a8 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -17,17 +17,14 @@
 
 import android.app.Activity;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.sysprop.SetupWizardProperties;
 import android.text.TextUtils;
 import android.view.View;
+import android.widget.Button;
 import android.widget.TextView;
 
-import com.google.android.setupcompat.template.FooterBarMixin;
-import com.google.android.setupcompat.template.FooterButton;
-import com.google.android.setupdesign.GlifLayout;
-import com.google.android.setupdesign.util.ThemeResolver;
+import com.android.setupwizardlib.util.WizardManagerHelper;
 
 /**
  * Activity that gives a user the choice to download the SIM app or defer until a later time
@@ -38,7 +35,7 @@
  * Can display the carrier app name if its passed into the intent with key
  * {@link #BUNDLE_KEY_CARRIER_NAME}
  */
-public class InstallCarrierAppActivity extends Activity {
+public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
     /**
      * Key for the carrier app name that will be displayed as the app to download.  If unset, a
      * default description will be used
@@ -53,33 +50,20 @@
     protected void onCreate(Bundle icicle) {
         // Setup theme for aosp/pixel
         setTheme(
-                new ThemeResolver.Builder()
-                        .setDefaultTheme(R.style.SudThemeGlifV3_Light)
-                        .build()
-                        .resolve(SetupWizardProperties.theme().orElse(""),
-                                /* suppressDayNight= */ false));
+                WizardManagerHelper.getThemeRes(
+                        SetupWizardProperties.theme().orElse(""),
+                        R.style.SuwThemeGlif_Light
+                )
+        );
 
         super.onCreate(icicle);
         setContentView(R.layout.install_carrier_app_activity);
 
-        GlifLayout layout = findViewById(R.id.setup_wizard_layout);
-        FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
-        mixin.setSecondaryButton(
-                new FooterButton.Builder(this)
-                        .setText(R.string.install_carrier_app_defer_action)
-                        .setListener(this::onSkipButtonClick)
-                        .setButtonType(FooterButton.ButtonType.SKIP)
-                        .setTheme(R.style.SudGlifButton_Secondary)
-                        .build());
+        Button notNowButton = findViewById(R.id.skip_button);
+        notNowButton.setOnClickListener(this);
 
-        mixin.setPrimaryButton(
-                new FooterButton.Builder(this)
-                        .setText(R.string.install_carrier_app_download_action)
-                        .setListener(this::onDownloadButtonClick)
-                        .setButtonType(FooterButton.ButtonType.OTHER)
-                        .setTheme(R.style.SudGlifButton_Primary)
-                        .build());
-
+        Button downloadButton = findViewById(R.id.download_button);
+        downloadButton.setOnClickListener(this);
 
         // Show/hide illo depending on whether one was provided in a resource overlay
         boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo);
@@ -98,17 +82,15 @@
     }
 
     @Override
-    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
-        theme.applyStyle(R.style.SetupWizardPartnerResource, true);
-        super.onApplyThemeResource(theme, resid, first);
-    }
-
-    protected void onSkipButtonClick(View view) {
-        finish(DEFER_RESULT);
-    }
-
-    protected void onDownloadButtonClick(View view) {
-        finish(DOWNLOAD_RESULT);
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.skip_button:
+                finish(DEFER_RESULT);
+                break;
+            case R.id.download_button:
+                finish(DOWNLOAD_RESULT);
+                break;
+        }
     }
 
     private void finish(int resultCode) {
diff --git a/packages/SoundPicker/res/values-cs/strings.xml b/packages/SoundPicker/res/values-cs/strings.xml
index e8fc97e..dc67c96 100644
--- a/packages/SoundPicker/res/values-cs/strings.xml
+++ b/packages/SoundPicker/res/values-cs/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="ringtone_default" msgid="798836092118824500">"Výchozí vyzváněcí tón"</string>
+    <string name="ringtone_default" msgid="798836092118824500">"Výchozí vyzvánění"</string>
     <string name="notification_sound_default" msgid="8133121186242636840">"Výchozí zvuk oznámení"</string>
     <string name="alarm_sound_default" msgid="4787646764557462649">"Výchozí zvuk budíku"</string>
-    <string name="add_ringtone_text" msgid="6642389991738337529">"Přidat vyzváněcí tón"</string>
+    <string name="add_ringtone_text" msgid="6642389991738337529">"Přidat vyzvánění"</string>
     <string name="add_alarm_text" msgid="3545497316166999225">"Přidat budík"</string>
     <string name="add_notification_text" msgid="4431129543300614788">"Přidat oznámení"</string>
     <string name="delete_ringtone_text" msgid="201443984070732499">"Smazat"</string>
-    <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Vlastní vyzváněcí tón se nepodařilo přidat"</string>
-    <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Vlastní vyzváněcí tón se nepodařilo smazat"</string>
+    <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Vlastní vyzvánění se nepodařilo přidat"</string>
+    <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Vlastní vyzvánění se nepodařilo smazat"</string>
     <string name="app_label" msgid="3091611356093417332">"Zvuky"</string>
 </resources>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2fbd9ba..a9a5671 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -22,6 +22,10 @@
     proto: {
         type: "nano",
     },
+
+    libs: [
+        "WindowManager-Shell-proto",
+    ],
 }
 
 java_library {
@@ -174,6 +178,9 @@
     kotlincflags: ["-Xjvm-default=enable"],
 
     dxflags: ["--multi-dex"],
-    required: ["privapp_whitelist_com.android.systemui"],
+    required: [
+        "privapp_whitelist_com.android.systemui",
+        "checked-wm_shell_protolog.json",
+    ],
 
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index af008b9..06a97b1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -390,7 +390,7 @@
         </activity-alias>
 
         <activity
-            android:name=".stackdivider.ForcedResizableInfoActivity"
+            android:name="com.android.wm.shell.splitscreen.ForcedResizableInfoActivity"
             android:theme="@style/ForcedResizableTheme"
             android:excludeFromRecents="true"
             android:stateNotNeeded="true"
@@ -516,7 +516,7 @@
             android:excludeFromRecents="true"
             android:visibleToInstantApps="true"/>
 
-        <!-- started from PipUI -->
+        <!-- started from PipController -->
         <activity
             android:name=".pip.tv.PipMenuActivity"
             android:permission="com.android.systemui.permission.SELF"
@@ -530,20 +530,6 @@
             androidprv:alwaysFocusable="true"
             android:excludeFromRecents="true" />
 
-        <activity
-            android:name=".pip.phone.PipMenuActivity"
-            android:permission="com.android.systemui.permission.SELF"
-            android:theme="@style/PipPhoneOverlayControlTheme"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:stateNotNeeded="true"
-            android:taskAffinity=""
-            android:launchMode="singleTop"
-            androidprv:alwaysFocusable="true" />
-
         <!-- started from SliceProvider -->
         <activity android:name=".SlicePermissionActivity"
             android:theme="@style/Theme.SystemUI.Dialog.Alert"
@@ -725,10 +711,9 @@
         <service android:name=".controls.controller.AuxiliaryPersistenceWrapper$DeletionJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE"/>
 
-        <!-- started from ControlsFavoritingActivity -->
+        <!-- started from ControlsRequestReceiver -->
         <activity
             android:name=".controls.management.ControlsRequestDialog"
-            android:exported="true"
             android:theme="@style/Theme.ControlsRequestDialog"
             android:finishOnCloseSystemDialogs="true"
             android:showForAllUsers="true"
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index 148fabb..ee8d023 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -111,14 +111,6 @@
 
 Shows UI for keyboard shortcuts (triggered by keyboard shortcut).
 
-### [com.android.systemui.onehanded.OneHandedUI](/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java)
-
-Shows the overlay controls when One handed is triggered.
-
-### [com.android.systemui.pip.PipUI](/packages/SystemUI/src/com/android/systemui/pip/PipUI.java)
-
-Shows the overlay controls when Pip is showing.
-
 ### [com.android.systemui.shortcut.ShortcutKeyDispatcher](/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java)
 
 Dispatches shortcut to System UI components.
diff --git a/packages/SystemUI/docs/camera.md b/packages/SystemUI/docs/camera.md
new file mode 100644
index 0000000..7a7a5aa
--- /dev/null
+++ b/packages/SystemUI/docs/camera.md
@@ -0,0 +1,33 @@
+# How double-click power launches the camera
+
+_as of august 2020_
+
+
+## Sequence of events
+
+
+
+1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`). 
+2. Even though PWMgr has a lot of logic to detect all manner of power button multipresses and gestures, it also checks with GestureLauncherService, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java#358) the power key.
+3. GLS is responsible for the camera timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java#475) (which hands it off to SystemUI).
+4. Inside SystemUI, [onCameraLaunchDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3927) looks at the keyguard state and determines 
+    1. whether the camera is even allowed 
+    2. whether the screen is on; if not, we need to delay until that happens
+    3. whether the device is locked (defined as "keyuguard is showing").
+5. If the device is unlocked (no keyguard), the camera is launched immediately. [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3949).
+6. If the keyguard is up, however, [KeyguardBottomAreaView.launchCamera](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#477) takes over to handle the "secure camera" (a different intent, usually directing to the same app, but giving that app the cue to not allow access to the photo roll, etc).
+7. If the intent [would have to launch a resolver](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#480) (the user has multiple cameras installed and hasn’t chosen one to always launch for the `SECURE_CAMERA_INTENT`),
+    1. In order to show the resolver, the lockscreen "bouncer" (authentication method) [is first presented](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523).
+8. Otherwise (just one secure camera), [it is launched](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#501) (with some window animation gymnastics).
+
+
+## Which intent launches?
+
+
+
+*   If the keyguard is not showing (device is unlocked)
+    *   `KeyguardBottomAreaView.INSECURE_CAMERA_INTENT`, defined to be `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA`. 
+    *   [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3950) in StatusBar.java.
+*   If the keyguard is showing (device locked)
+    *   [KeyguardBottomAreaView.getCameraIntent()](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#366) is consulted, which allows the "keyguard right button" (which we don’t actually show) to control the camera intent. The [default implementation](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#831) returns one of `KeyguardBottomAreaView.INSECURE_CAMERA_INTENT` or `KeyguardBottomAreaView.SECURE_CAMERA_INTENT`, which are `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA` and `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE`, respectively.
+    *   [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523) in KeyguardBottomAreaView.java.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 9d52098..63f8b1f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -30,7 +30,7 @@
  */
 @ProvidesInterface(version = FalsingManager.VERSION)
 public interface FalsingManager {
-    int VERSION = 4;
+    int VERSION = 5;
 
     void onSuccessfulUnlock();
 
@@ -42,7 +42,8 @@
 
     boolean isUnlockingDisabled();
 
-    boolean isFalseTouch();
+    /** Returns true if the gesture should be rejected. */
+    boolean isFalseTouch(int interactionType);
 
     void onNotificatonStopDraggingDown();
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
index 02c4c5e..4b6efa9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
@@ -14,16 +14,16 @@
 
 package com.android.systemui.plugins.statusbar;
 
-import com.android.systemui.plugins.annotations.DependsOn;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+
 @ProvidesInterface(version = NotificationSwipeActionHelper.VERSION)
 @DependsOn(target = SnoozeOption.class)
 public interface NotificationSwipeActionHelper {
@@ -52,7 +52,8 @@
 
     public boolean isDismissGesture(MotionEvent ev);
 
-    public boolean isFalseGesture(MotionEvent ev);
+    /** Returns true if the gesture should be rejected. */
+    boolean isFalseGesture();
 
     public boolean swipedFarEnough(float translation, float viewSize);
 
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index df66bf5..6c06b0a 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -41,7 +41,12 @@
     public <init>(android.content.Context);
 }
 
+# Keep the wm shell lib
 -keep class com.android.wm.shell.*
+# Keep the protolog group methods that are called by the generated code
+-keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup {
+    *;
+}
 
 -keep class com.android.systemui.dagger.GlobalRootComponent { *; }
 -keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; }
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 6d86a78..65e3f0d 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -112,7 +112,7 @@
     <string name="kg_pin_accepted" msgid="1625501841604389716">"تم قبول الرمز"</string>
     <string name="keyguard_carrier_default" msgid="6359808469637388586">"لا تتوفر خدمة."</string>
     <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تبديل أسلوب الإدخال"</string>
-    <string name="airplane_mode" msgid="2528005343938497866">"وضع الطائرة"</string>
+    <string name="airplane_mode" msgid="2528005343938497866">"وضع الطيران"</string>
     <string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"يجب رسم النقش بعد إعادة تشغيل الجهاز"</string>
     <string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"يجب إدخال رقم التعريف الشخصي بعد إعادة تشغيل الجهاز"</string>
     <string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"يجب إدخال كلمة المرور بعد إعادة تشغيل الجهاز"</string>
diff --git a/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..998db3b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mcc310-mnc004/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,36 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml
new file mode 100644
index 0000000..998db3b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mcc311-mnc480/ic_5g_plus_mobiledata.xml
@@ -0,0 +1,36 @@
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:viewportWidth="22"
+    android:viewportHeight="17"
+    android:width="22dp"
+    android:height="17dp">
+  <group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M19.98,3.54v2.81c0,0.47 -0.15,0.84 -0.44,1.11s-0.69,0.41 -1.2,0.41c-0.5,0 -0.89,-0.13 -1.19,-0.4s-0.44,-0.63 -0.45,-1.09V3.54h0.88v2.82c0,0.28 0.07,0.48 0.2,0.61c0.13,0.13 0.32,0.19 0.56,0.19c0.49,0 0.75,-0.26 0.75,-0.78V3.54H19.98z"/>
+      <path android:fillColor="#FF000000"
+            android:pathData="M19.42,12.25l0.57,-3.04h0.88l-0.95,4.27h-0.88l-0.69,-2.85l-0.69,2.85h-0.88l-0.95,-4.27h0.88l0.58,3.03l0.7,-3.03h0.74L19.42,12.25z"/>
+    </group>
+    <group>
+      <path android:fillColor="#FF000000"
+          android:pathData="M0.94,8.49l0.43,-4.96H5.7v1.17H2.39L2.15,7.41c0.41,-0.29 0.85,-0.43 1.33,-0.43c0.77,0 1.38,0.3 1.83,0.9c0.44,0.6 0.66,1.41 0.66,2.43c0,1.03 -0.24,1.84 -0.72,2.43c-0.48,0.59 -1.14,0.88 -1.98,0.88c-0.75,0 -1.36,-0.24 -1.83,-0.73c-0.47,-0.49 -0.74,-1.16 -0.81,-2.02h1.13c0.07,0.57 0.23,1 0.49,1.29c0.26,0.29 0.59,0.43 1.01,0.43c0.47,0 0.84,-0.2 1.1,-0.61c0.26,-0.41 0.4,-0.96 0.4,-1.65c0,-0.65 -0.14,-1.18 -0.43,-1.59C4.05,8.32 3.67,8.11 3.19,8.11c-0.4,0 -0.72,0.1 -0.96,0.31L1.9,8.75L0.94,8.49z"/>
+    </group>
+    <path android:fillColor="#FF000000"
+        android:pathData="M13.86,12.24l-0.22,0.27c-0.63,0.73 -1.55,1.1 -2.76,1.1c-1.08,0 -1.92,-0.36 -2.53,-1.07c-0.61,-0.71 -0.93,-1.72 -0.94,-3.02V7.56c0,-1.39 0.28,-2.44 0.84,-3.13c0.56,-0.7 1.39,-1.04 2.51,-1.04c0.95,0 1.69,0.26 2.22,0.79c0.54,0.53 0.83,1.28 0.89,2.26h-1.25c-0.05,-0.62 -0.22,-1.1 -0.52,-1.45c-0.29,-0.35 -0.74,-0.52 -1.34,-0.52c-0.72,0 -1.24,0.23 -1.57,0.7C8.85,5.63 8.68,6.37 8.66,7.4v2.03c0,1 0.19,1.77 0.57,2.31c0.38,0.54 0.93,0.8 1.65,0.8c0.67,0 1.19,-0.16 1.54,-0.49l0.18,-0.17V9.59h-1.82V8.52h3.07V12.24z"/>
+  </group>
+</vector>
+
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
index 56d847c..8179bf4 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
@@ -24,23 +24,17 @@
 
     <FrameLayout
         android:id="@+id/volume_dialog"
-        android:minWidth="@dimen/volume_dialog_panel_width"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="right"
         android:background="@android:color/transparent"
-        android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right"
-        android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
-        android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
-        android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding"
+        android:padding="@dimen/volume_dialog_panel_transparent_padding"
         android:clipToPadding="false">
 
         <LinearLayout
             android:id="@+id/main"
             android:layout_width="wrap_content"
-            android:minWidth="@dimen/volume_dialog_panel_width"
             android:layout_height="wrap_content"
-            android:layout_marginTop="68dp"
             android:layout_gravity="right"
             android:orientation="vertical"
             android:translationZ="@dimen/volume_dialog_elevation"
@@ -52,7 +46,6 @@
                 android:id="@+id/volume_dialog_rows"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:minWidth="@dimen/volume_dialog_panel_width"
                 android:gravity="center"
                 android:orientation="horizontal">
                 <!-- volume rows added and removed here! :-) -->
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index c0f0aa8..d28d566 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -16,8 +16,8 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:tag="row"
-    android:layout_width="@dimen/volume_dialog_row_width"
-    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/volume_dialog_row_height"
     android:background="@android:color/transparent"
     android:clipChildren="false"
     android:clipToPadding="false"
@@ -30,15 +30,17 @@
         android:background="@android:color/transparent"
         android:gravity="center"
         android:layout_gravity="center"
-        android:orientation="horizontal" >
-        <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/volume_row_icon"
-            style="@style/VolumeButtons"
+        android:orientation="vertical" >
+        <TextView
+            android:id="@+id/volume_number"
             android:layout_width="@dimen/tv_volume_dialog_bubble_size"
             android:layout_height="@dimen/tv_volume_dialog_bubble_size"
+            android:maxLength="2"
+            android:gravity="center"
+            android:fontFeatureSettings="tnum"
             android:background="@drawable/tv_volume_dialog_circle"
-            android:tint="@color/accent_tint_color_selector"
-            android:soundEffectsEnabled="false" />
+            android:textSize="@dimen/tv_volume_number_text_size"
+            android:textColor="@color/accent_tint_color_selector"/>
         <TextView
             android:id="@+id/volume_row_header"
             android:layout_width="wrap_content"
@@ -51,27 +53,24 @@
             android:textAppearance="@style/TextAppearance.Volume.Header" />
         <FrameLayout
             android:id="@+id/volume_row_slider_frame"
-            android:layout_height="match_parent"
-            android:layoutDirection="ltr"
-            android:layout_width="@dimen/volume_dialog_row_width">
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/volume_dialog_row_height">
             <SeekBar
                 android:id="@+id/volume_row_slider"
                 android:clickable="false"
-                android:layout_width="@dimen/volume_dialog_row_width"
+                android:layout_width="@dimen/volume_dialog_row_height"
                 android:layout_height="match_parent"
-                android:layoutDirection="ltr"
                 android:layout_gravity="center"
-                android:rotation="0" />
+                android:rotation="270" />
         </FrameLayout>
-        <TextView
-            android:id="@+id/volume_number"
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/volume_row_icon"
+            style="@style/VolumeButtons"
             android:layout_width="@dimen/tv_volume_dialog_bubble_size"
             android:layout_height="@dimen/tv_volume_dialog_bubble_size"
-            android:maxLength="2"
-            android:gravity="center"
             android:background="@drawable/tv_volume_dialog_circle"
-            android:textSize="@dimen/tv_volume_number_text_size"
-            android:textColor="@color/accent_tint_color_selector"/>
+            android:tint="@color/accent_tint_color_selector"
+            android:soundEffectsEnabled="false" />
     </LinearLayout>
 
     <include layout="@layout/volume_dnd_icon"/>
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
index ae7f44d..b9e711e 100644
--- a/packages/SystemUI/res/layout/controls_management.xml
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -50,7 +50,7 @@
 
     <FrameLayout
         android:layout_width="match_parent"
-        android:layout_height="72dp">
+        android:layout_height="@dimen/controls_management_footer_height">
 
         <View
             android:layout_width="match_parent"
@@ -61,7 +61,8 @@
         <androidx.constraintlayout.widget.ConstraintLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:padding="@dimen/controls_management_footer_side_margin">
+            android:paddingHorizontal="@dimen/controls_management_footer_side_margin"
+            android:paddingVertical="@dimen/controls_management_footer_top_margin" >
 
             <Button
                 android:id="@+id/other_apps"
diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml
index 4850e75..0ddd0e38 100644
--- a/packages/SystemUI/res/layout/controls_management_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_management_favorites.xml
@@ -36,7 +36,7 @@
         android:layout_width="wrap_content"
         android:layout_height="@dimen/controls_management_page_indicator_height"
         android:layout_gravity="center"
-        android:layout_marginTop="@dimen/controls_management_list_margin"
+        android:layout_marginTop="@dimen/controls_management_indicator_top_margin"
         android:visibility="invisible" />
 
     <androidx.viewpager2.widget.ViewPager2
diff --git a/packages/SystemUI/res/layout/controls_structure_page.xml b/packages/SystemUI/res/layout/controls_structure_page.xml
index f048d62..412ed56 100644
--- a/packages/SystemUI/res/layout/controls_structure_page.xml
+++ b/packages/SystemUI/res/layout/controls_structure_page.xml
@@ -21,4 +21,4 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:layout_marginTop="@dimen/controls_management_zone_top_margin"/>
\ No newline at end of file
+    android:layout_marginTop="@dimen/controls_management_favorites_top_margin"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index ed870f8..170f2c4 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -166,8 +166,7 @@
         android:layout_height="wrap_content"
         android:clickable="true"
         android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp"
+        android:paddingVertical="@dimen/qs_media_enabled_seekbar_vertical_padding"
         android:thumbTint="@color/media_primary_text"
         android:progressTint="@color/media_seekbar_progress"
         android:progressBackgroundTint="@color/media_disabled"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 316fa8a..eae2cbb 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Wys laeprioriteit-kennisgewingikone"</string>
     <string name="other" msgid="429768510980739978">"Ander"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Skermverdeler"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Volskerm links"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Links 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Links 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Links 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Volskerm regs"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Volskerm bo"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Bo 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Bo 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Bo 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Volskerm onder"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dubbeltik om te wysig."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om by te voeg."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Skuif <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Skuif <xliff:g id="TILE_NAME">%1$s</xliff:g> na posisie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-kennisgewing: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Program sal dalk nie met verdeelde skerm werk nie."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Program steun nie verdeelde skerm nie."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Program steun nie begin op sekondêre skerms nie."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Maak instellings oop."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Maak kitsinstellings oop."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Maak kitsinstellings toe."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index e2a4dc6..e8326a7 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"አነስተኛ ቅድሚያ ያላቸው የማሳወቂያ አዶዎችን አሳይ"</string>
     <string name="other" msgid="429768510980739978">"ሌላ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"የተከፈለ የማያ ገጽ ከፋይ"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"የግራ ሙሉ ማያ ገጽ"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ግራ 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ግራ 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ግራ 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"የቀኝ ሙሉ ማያ ገጽ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"የላይ ሙሉ ማያ ገጽ"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ከላይ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ከላይ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ከላይ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"የታች ሙሉ ማያ ገጽ"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ቦታ <xliff:g id="POSITION">%1$d</xliff:g>፣ <xliff:g id="TILE_NAME">%2$s</xliff:g>። ለማርትዕ ሁለቴ መታ ያድርጉ።"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>። ለማከል ሁለቴ መታ ያድርጉ።"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ይውሰዱ"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ወደ አቀማመጥ <xliff:g id="POSITION">%2$d</xliff:g> አንቀሳቅስ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"የ<xliff:g id="ID_1">%1$s</xliff:g> ማሳወቂያ፦ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"መተግበሪያ በሁለተኛ ማሳያዎች ላይ ማስጀመርን አይደግፍም።"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ቅንብሮችን ክፈት።"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ፈጣን ቅንብሮችን ክፈት።"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ፈጣን ቅንብሮችን ዝጋ።"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b33c6c5..b020f17 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -232,7 +232,7 @@
     <string name="not_default_data_content_description" msgid="6757881730711522517">"لم يتم الضبط على استخدام البيانات"</string>
     <string name="cell_data_off" msgid="4886198950247099526">"غير مفعّلة"</string>
     <string name="accessibility_bluetooth_tether" msgid="6327291292208790599">"التوصيل عبر البلوتوث"</string>
-    <string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطائرة."</string>
+    <string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطيران."</string>
     <string name="accessibility_vpn_on" msgid="8037549696057288731">"‏الشبكة الافتراضية الخاصة (VPN) قيد التفعيل."</string>
     <string name="accessibility_no_sims" msgid="5711270400476534667">"‏ليس هناك شريحة SIM."</string>
     <string name="carrier_network_change_mode" msgid="5174141476991149918">"جارٍ تغيير شبكة مشغِّل شبكة الجوّال."</string>
@@ -267,10 +267,10 @@
     <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"‏تم تفعيل Wifi."</string>
     <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"الجوّال <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"البطارية <xliff:g id="STATE">%s</xliff:g>."</string>
-    <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"إيقاف وضع الطائرة."</string>
-    <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"تفعيل وضع الطائرة."</string>
-    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"تم إيقاف وضع الطائرة."</string>
-    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"تم تفعيل وضع الطائرة."</string>
+    <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"إيقاف وضع الطيران."</string>
+    <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"تفعيل وضع الطيران."</string>
+    <string name="accessibility_quick_settings_airplane_changed_off" msgid="8880183481476943754">"تم إيقاف وضع الطيران."</string>
+    <string name="accessibility_quick_settings_airplane_changed_on" msgid="6327378061894076288">"تم تفعيل وضع الطيران."</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"كتم الصوت تمامًا"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"المنبِّهات فقط"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"عدم الإزعاج"</string>
@@ -664,7 +664,7 @@
     <string name="status_bar_ethernet" msgid="5690979758988647484">"إيثرنت"</string>
     <string name="status_bar_alarm" msgid="87160847643623352">"المنبّه"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
-    <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطائرة"</string>
+    <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
     <string name="add_tile" msgid="6239678623873086686">"إضافة فئة"</string>
     <string name="broadcast_tile" msgid="5224010633596487481">"إرسال فئة"</string>
     <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g> إلا إذا أوقفت هذا قبل الموعد"</string>
@@ -899,17 +899,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
     <string name="other" msgid="429768510980739978">"غير ذلك"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"أداة تقسيم الشاشة"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"عرض النافذة اليسرى بملء الشاشة"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ضبط حجم النافذة اليسرى ليكون ٧٠%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ضبط حجم النافذة اليسرى ليكون ٥٠%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ضبط حجم النافذة اليسرى ليكون ٣٠%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"عرض النافذة اليمنى بملء الشاشة"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"عرض النافذة العلوية بملء الشاشة"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ضبط حجم النافذة العلوية ليكون ٧٠%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ضبط حجم النافذة العلوية ليكون ٥٠%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ضبط حجم النافذة العلوية ليكون ٣٠%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"عرض النافذة السفلية بملء الشاشة"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"الموضع <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>. انقر مرّتين للتعديل."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. انقر مرّتين للإضافة."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"نقل <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -918,10 +907,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"نقل <xliff:g id="TILE_NAME">%1$s</xliff:g> إلى الموضع <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"إشعار <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"يمكن ألا يعمل التطبيق مع وضع تقسيم الشاشة."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"التطبيق لا يتيح تقسيم الشاشة."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"لا يمكن تشغيل التطبيق على شاشات عرض ثانوية."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"فتح الإعدادات."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"فتح الإعدادات السريعة."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"إغلاق الإعدادات السريعة."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 5a3c659..31077da 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"কম গুৰুত্বপূৰ্ণ জাননীৰ আইকনসমূহ দেখুৱাওক"</string>
     <string name="other" msgid="429768510980739978">"অন্যান্য"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"স্প্লিট স্ক্ৰীণৰ বিভাজক"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"বাওঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"বাওঁফালৰ স্ক্ৰীণখন ৭০% কৰক"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"বাওঁফালৰ স্ক্ৰীণখন ৫০% কৰক"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"বাওঁফালৰ স্ক্ৰীণখন ৩০% কৰক"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"সোঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"শীৰ্ষ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"শীর্ষ স্ক্ৰীণখন ৭০% কৰক"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"শীর্ষ স্ক্ৰীণখন ৫০% কৰক"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"শীর্ষ স্ক্ৰীণখন ৩০% কৰক"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"তলৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"অৱস্থান <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। সম্পাদনা কৰিবৰ বাবে দুবাৰ টিপক।"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। যোগ কৰিবলৈ দুবাৰ টিপক।"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> স্থানান্তৰ কৰক"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ক এই স্থান <xliff:g id="POSITION">%2$d</xliff:g>লৈ যাওক"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"বিভাজিত স্ক্ৰীণৰ সৈতে এপে হয়তো কাম নকৰিব।"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"গৌণ ডিছপ্লেত এপ্ লঞ্চ কৰিব নোৱাৰি।"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ক্ষিপ্ৰ ছেটিংসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ক্ষিপ্ৰ ছেটিংসমূহ বন্ধ কৰক।"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 4aa4bda..b596066 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -403,7 +403,7 @@
       <item quantity="one">%d cihaz</item>
     </plurals>
     <string name="quick_settings_notifications_label" msgid="3379631363952582758">"Bildirişlər"</string>
-    <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"İşartı"</string>
+    <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Fənər"</string>
     <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera istifadə olunur"</string>
     <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobil data"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Data istifadəsi"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Aşağı prioritet bildiriş işarələrini göstərin"</string>
     <string name="other" msgid="429768510980739978">"Digər"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Bölünmüş ekran ayırıcısı"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Sol tam ekran"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Sol 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Sol 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Sol 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Sağ tam ekran"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Yuxarı tam ekran"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Yuxarı 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Yuxarı 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Yuxarı 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Aşağı tam ekran"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Redaktə etmək üçün iki dəfə tıklayın."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Əlavə etmək üçün iki dəfə tıklayın."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> köçürün"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> pozisiyasına <xliff:g id="TILE_NAME">%1$s</xliff:g> köçürün"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildiriş: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Tətbiq ikinci ekranda işləməyə bilər."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Tətbiq ikinci ekranda başlamağı dəstəkləmir."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları açın."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Cəld ayarları açın."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Cəld ayarları bağlayın."</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index a9725f3..aa141e6 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -884,17 +884,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obaveštenja niskog prioriteta"</string>
     <string name="other" msgid="429768510980739978">"Drugo"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Razdelnik podeljenog ekrana"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Režim celog ekrana za levi ekran"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Levi ekran 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Levi ekran 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Levi ekran 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Režim celog ekrana za donji ekran"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Režim celog ekrana za gornji ekran"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Gornji ekran 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gornji ekran 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gornji ekran 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Režim celog ekrana za donji ekran"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvaput dodirnite da biste izmenili."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dvaput dodirnite da biste dodali."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premesti pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -903,10 +892,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premestite „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obaveštenja za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija možda neće funkcionisati sa podeljenim ekranom."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikacija ne podržava podeljeni ekran."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori Podešavanja."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Otvori Brza podešavanja."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zatvori Brza podešavanja."</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index b4c5eba..194f92b 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Паказваць значкі апавяшчэнняў з нізкім прыярытэтам"</string>
     <string name="other" msgid="429768510980739978">"Іншае"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Раздзяляльнік падзеленага экрана"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Левы экран – поўнаэкранны рэжым"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Левы экран – 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Левы экран – 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Левы экран – 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Правы экран – поўнаэкранны рэжым"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Верхні экран – поўнаэкранны рэжым"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Верхні экран – 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Верхні экран – 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Верхні экран – 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ніжні экран – поўнаэкранны рэжым"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Месца: <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Краніце двойчы, каб рэдагаваць."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Краніце двойчы, каб дадаць."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Перамясціць <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Перамясціць \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" у наступнае месца: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Апавяшчэнне <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Праграма можа не працаваць у рэжыме дзялення экрана."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Праграма можа не працаваць на дадатковых экранах."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Праграма не падтрымлівае запуск на дадатковых экранах."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Адкрыць налады."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Адкрыць хуткія налады."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Закрыць хуткія налады."</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 52e8286..02eea8a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Показване на иконите за известията с нисък приоритет"</string>
     <string name="other" msgid="429768510980739978">"Друго"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Разделител в режима за разделен екран"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Ляв екран: Показване на цял екран"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Ляв екран: 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Ляв екран: 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Ляв екран: 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Десен екран: Показване на цял екран"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Горен екран: Показване на цял екран"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Горен екран: 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Горен екран: 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Горен екран: 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Долен екран: Показване на цял екран"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>, „<xliff:g id="TILE_NAME">%2$s</xliff:g>“. Докоснете двукратно, за да редактирате."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"„<xliff:g id="TILE_NAME">%1$s</xliff:g>“. Докоснете двукратно, за да добавите."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Преместване на „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместете „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позиция <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известие от <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Приложението може да не работи в режим на разделен екран."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Приложението не поддържа разделен екран."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Приложението не поддържа използването на алтернативни дисплеи."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отваряне на настройките."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Отваряне на бързите настройки."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Затваряне на бързите настройки."</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index d0de9cf..96a7368 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"কম-গুরুত্বপূর্ণ বিজ্ঞপ্তির আইকন দেখুন"</string>
     <string name="other" msgid="429768510980739978">"অন্যান্য"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"বিভক্ত-স্ক্রিন বিভাজক"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"বাঁ দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"৭০% বাকি আছে"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"৫০% বাকি আছে"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"৩০% বাকি আছে"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"ডান দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"উপর দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"শীর্ষ ৭০%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"শীর্ষ ৫০%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"শীর্ষ ৩০%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"নীচের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> লোকেশন, <xliff:g id="TILE_NAME">%2$s</xliff:g>৷ সম্পাদনা করতে দুবার আলতো চাপুন৷"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>৷ যোগ করতে দুবার আলতো চাপুন৷"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> সরান"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>-এ সরান"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> বিজ্ঞপ্তি: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"অ্যাপ্লিকেশানটি বিভক্ত স্ক্রীনে কাজ নাও করতে পারে৷"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"সেকেন্ডারি ডিসপ্লেতে অ্যাপ লঞ্চ করা যাবে না।"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"সেটিংস খুলুন।"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"দ্রুত সেটিংস খুলুন৷"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"দ্রুত সেটিংস বন্ধ করুন৷"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 57b7945..0a091aa 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -884,17 +884,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavještenja niskog prioriteta"</string>
     <string name="other" msgid="429768510980739978">"Ostalo"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Razdjelnik ekrana"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Lijevo cijeli ekran"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Lijevo 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Lijevo 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Lijevo 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Desno cijeli ekran"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Gore cijeli ekran"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Gore 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gore 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gore 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Donji ekran kao cijeli ekran"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dodirnite dvaput za uređivanje."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> Dodirnite dvaput za dodavanje."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pomjeri <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -903,10 +892,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premjesti <xliff:g id="TILE_NAME">%1$s</xliff:g> na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavještenje: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija možda neće raditi na podijeljenom ekranu"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikacija ne podržava dijeljenje ekrana."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori postavke."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Otvoriti brze postavke."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zatvoriti brze postavke."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 0489d18..d4e8bef 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -707,7 +707,7 @@
     <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactiva les notificacions"</string>
     <string name="inline_keep_showing_app" msgid="4393429060390649757">"Vols continuar rebent notificacions d\'aquesta aplicació?"</string>
     <string name="notification_silence_title" msgid="8608090968400832335">"Silenci"</string>
-    <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
+    <string name="notification_alert_title" msgid="3656229781017543655">"Predeterminat"</string>
     <string name="notification_bubble_title" msgid="8330481035191903164">"Bombolla"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"Automàtic"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Sense so ni vibració"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
     <string name="other" msgid="429768510980739978">"Altres"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Divisor de pantalles"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Pantalla esquerra completa"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Pantalla esquerra al 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Pantalla esquerra al 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Pantalla esquerra al 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Pantalla dreta completa"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Pantalla superior completa"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Pantalla superior al 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Pantalla superior al 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Pantalla superior al 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla inferior completa"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posició <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Fes doble toc per editar-la."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Fes doble toc per afegir-ho."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mou <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mou <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"L\'aplicació no admet la pantalla dividida."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"L\'aplicació no es pot obrir en pantalles secundàries."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Obre la configuració."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Obre la configuració ràpida."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Tanca la configuració ràpida."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 121a81d..239a0f6 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -95,7 +95,7 @@
     <string name="screenrecord_description" msgid="1123231719680353736">"Při nahrávání může systém Android zaznamenávat citlivé údaje, které jsou viditelné na obrazovce nebo které jsou přehrávány na zařízení. Týká se to hesel, údajů o platbě, fotek, zpráv a zvuků."</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrát zvuk"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk zařízení"</string>
-    <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk ze zařízení, například hudba, hovory a vyzváněcí tóny"</string>
+    <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk ze zařízení, například hudba, hovory a vyzvánění"</string>
     <string name="screenrecord_mic_label" msgid="2111264835791332350">"Mikrofon"</string>
     <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Zvuk a mikrofon zařízení"</string>
     <string name="screenrecord_start" msgid="330991441575775004">"Spustit"</string>
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Zobrazit ikony oznámení s nízkou prioritou"</string>
     <string name="other" msgid="429768510980739978">"Jiné"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Čára rozdělující obrazovku"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Levá část na celou obrazovku"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70 % vlevo"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50 % vlevo"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30 % vlevo"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Pravá část na celou obrazovku"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Horní část na celou obrazovku"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70 % nahoře"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % nahoře"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % nahoře"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Dolní část na celou obrazovku"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvojitým klepnutím ji upravíte."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dlaždici přidáte dvojitým klepnutím."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Přesunout dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Přesunout dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozici <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikace na sekundárním displeji nemusí fungovat."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikace nepodporuje spuštění na sekundárních displejích."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Otevřít rychlé nastavení."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zavřít rychlé nastavení."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 8863695..7ce60ea 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for notifikationer med lav prioritet"</string>
     <string name="other" msgid="429768510980739978">"Andet"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Adskiller til opdelt skærm"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Vis venstre del i fuld skærm"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Venstre 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Venstre 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Venstre 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Vis højre del i fuld skærm"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Vis øverste del i fuld skærm"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Øverste 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Øverste 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Øverste 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Vis nederste del i fuld skærm"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tryk to gange for at redigere."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tryk to gange for at tilføje."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Appen fungerer muligvis ikke i opdelt skærm."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Appen understøtter ikke opdelt skærm."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Appen fungerer muligvis ikke på sekundære skærme."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Appen kan ikke åbnes på sekundære skærme."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åbn Indstillinger."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Åbn Kvikmenu."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Luk Kvikmenu."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 8d990c6..093e1a2 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Symbole für Benachrichtigungen mit einer niedrigen Priorität anzeigen"</string>
     <string name="other" msgid="429768510980739978">"Sonstiges"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Bildschirmteiler"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Vollbild links"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70 % links"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50 % links"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30 % links"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Vollbild rechts"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Vollbild oben"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70 % oben"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % oben"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % oben"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Vollbild unten"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Zum Bearbeiten doppeltippen."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Zum Hinzufügen doppeltippen."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verschieben"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> auf Position <xliff:g id="POSITION">%2$d</xliff:g> verschieben"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Benachrichtigung von <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Die App funktioniert unter Umständen bei geteiltem Bildschirm nicht."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Die App unterstützt den Start auf sekundären Displays nicht."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Einstellungen öffnen."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Schnelleinstellungen öffnen."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Schnelleinstellungen schließen."</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 4384749..ac1a3ee 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -691,7 +691,7 @@
     <string name="notification_channel_minimized" msgid="6892672757877552959">"Αυτές οι ειδοποιήσεις θα ελαχιστοποιηθούν"</string>
     <string name="notification_channel_silenced" msgid="1995937493874511359">"Αυτές οι ειδοποιήσεις θα εμφανίζονται σιωπηλά"</string>
     <string name="notification_channel_unsilenced" msgid="94878840742161152">"Αυτές οι ειδοποιήσεις θα σας ενημερώνουν"</string>
-    <string name="inline_blocking_helper" msgid="2891486013649543452">"Συνήθως απορρίπτετε αυτές τις ειδοποιήσεις. \nΝα εξακολουθήσουν να εμφανίζονται;"</string>
+    <string name="inline_blocking_helper" msgid="2891486013649543452">"Συνήθως παραβλέπετε αυτές τις ειδοποιήσεις. \nΝα εξακολουθήσουν να εμφανίζονται;"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Τέλος"</string>
     <string name="inline_ok_button" msgid="603075490581280343">"Εφαρμογή"</string>
     <string name="inline_keep_showing" msgid="8736001253507073497">"Να συνεχίσουν να εμφανίζονται αυτές οι ειδοποιήσεις;"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Εμφάνιση εικονιδίων ειδοποιήσεων χαμηλής προτεραιότητας"</string>
     <string name="other" msgid="429768510980739978">"Άλλο"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Διαχωριστικό οθόνης"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Αριστερή πλήρης οθόνη"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Αριστερή 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Αριστερή 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Αριστερή 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Δεξιά πλήρης οθόνη"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Πάνω πλήρης οθόνη"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Πάνω 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Πάνω 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Πάνω 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Κάτω πλήρης οθόνη"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Πατήστε δύο φορές για επεξεργασία."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Πατήστε δύο φορές για προσθήκη."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Μετακίνηση <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Μετακίνηση <xliff:g id="TILE_NAME">%1$s</xliff:g> στη θέση <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ειδοποίηση <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Δεν είναι δυνατή η λειτουργία της εφαρμογής με διαχωρισμό οθόνης."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Η εφαρμογή δεν υποστηρίζει την εκκίνηση σε δευτερεύουσες οθόνες."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Άνοιγμα ρυθμίσεων."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Άνοιγμα γρήγορων ρυθμίσεων."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Κλείσιμο γρήγορων ρυθμίσεων."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 8a7963a..3a76d75 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
     <string name="other" msgid="429768510980739978">"Other"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Split screen divider"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Left full screen"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Left 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Left 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Left 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Right full screen"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Top full screen"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Top 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"App does not support split-screen."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"App may not work on a secondary display."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"App does not support launch on secondary displays."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Open quick settings."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Close quick settings."</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 7b2b25a..ac65193 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
     <string name="other" msgid="429768510980739978">"Other"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Split screen divider"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Left full screen"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Left 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Left 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Left 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Right full screen"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Top full screen"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Top 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"App does not support split-screen."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"App may not work on a secondary display."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"App does not support launch on secondary displays."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Open quick settings."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Close quick settings."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 8a7963a..3a76d75 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
     <string name="other" msgid="429768510980739978">"Other"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Split screen divider"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Left full screen"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Left 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Left 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Left 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Right full screen"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Top full screen"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Top 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"App does not support split-screen."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"App may not work on a secondary display."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"App does not support launch on secondary displays."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Open quick settings."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Close quick settings."</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 8a7963a..3a76d75 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
     <string name="other" msgid="429768510980739978">"Other"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Split screen divider"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Left full screen"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Left 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Left 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Left 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Right full screen"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Top full screen"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Top 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Top 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Top 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Bottom full screen"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"App may not work with split-screen."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"App does not support split-screen."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"App may not work on a secondary display."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"App does not support launch on secondary displays."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Open quick settings."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Close quick settings."</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 15738f2..211a27c 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‎Show low-priority notification icons‎‏‎‎‏‎"</string>
     <string name="other" msgid="429768510980739978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎Other‎‏‎‎‏‎"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‏‎Split-screen divider‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎Left full screen‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎Left 70%‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‎Left 50%‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎Left 30%‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎Right full screen‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎Top full screen‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎Top 70%‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‎‏‎‏‎Top 50%‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎Top 30%‎‏‎‎‏‎"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎Bottom full screen‎‏‎‎‏‎"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‏‎Position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎. Double tap to edit.‎‏‎‎‏‎"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. Double tap to add.‎‏‎‎‏‎"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎Move ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎Move ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%2$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎Quick settings editor.‎‏‎‎‏‎"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="ID_1">%1$s</xliff:g>‎‏‎‎‏‏‏‎ notification: ‎‏‎‎‏‏‎<xliff:g id="ID_2">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎App may not work with split-screen.‎‏‎‎‏‎"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‎‎App does not support split-screen.‎‏‎‎‏‎"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‎‎‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎App does not support launch on secondary displays.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎Open settings.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎Open quick settings.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎Close quick settings.‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index aa81ab8..ccc4626 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
     <string name="other" msgid="429768510980739978">"Otros"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Divisor de pantalla dividida"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Pantalla izquierda completa"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Izquierda: 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Izquierda: 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Izquierda: 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Pantalla derecha completa"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Pantalla superior completa"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Superior: 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Superior: 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Superior: 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla inferior completa"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Presiona dos veces para editarla."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Presiona dos veces para agregarlo."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Es posible que la app no funcione en el modo de pantalla dividida."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"La app no es compatible con la función de pantalla dividida."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Es posible que la app no funcione en una pantalla secundaria."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"La app no puede iniciarse en pantallas secundarias."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir Configuración"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Abrir la configuración rápida"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Cerrar configuración rápida"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 8638570..4e9347c 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -98,7 +98,7 @@
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sonido de tu dispositivo, como música, llamadas y tonos de llamada"</string>
     <string name="screenrecord_mic_label" msgid="2111264835791332350">"Micrófono"</string>
     <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Audio y micrófono del dispositivo"</string>
-    <string name="screenrecord_start" msgid="330991441575775004">"Empezar"</string>
+    <string name="screenrecord_start" msgid="330991441575775004">"Iniciar"</string>
     <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Grabando pantalla"</string>
     <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Grabando pantalla y audio"</string>
     <string name="screenrecord_taps_label" msgid="1595690528298857649">"Mostrar toques en la pantalla"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string>
     <string name="other" msgid="429768510980739978">"Otros"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Dividir la pantalla"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Pantalla izquierda completa"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Izquierda 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Izquierda 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Izquierda 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Pantalla derecha completa"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Pantalla superior completa"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Superior 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Superior 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Superior 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla inferior completa"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toca dos veces para cambiarla."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dos veces para añadirlo."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Es posible que la aplicación no funcione con la pantalla dividida."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"La aplicación no admite la pantalla dividida."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"La aplicación no se puede abrir en pantallas secundarias."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Abrir ajustes rápidos."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Cerrar ajustes rápidos."</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index f5ffad2..21189f1 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Kuva madala prioriteediga märguande ikoonid"</string>
     <string name="other" msgid="429768510980739978">"Muu"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Ekraanijagaja"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Vasak täisekraan"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Vasak: 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Vasak: 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Vasak: 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Parem täisekraan"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Ülemine täisekraan"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Ülemine: 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Ülemine: 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Ülemine: 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Alumine täisekraan"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Asend <xliff:g id="POSITION">%1$d</xliff:g>, paan <xliff:g id="TILE_NAME">%2$s</xliff:g>. Topeltpuudutage muutmiseks."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Topeltpuudutage lisamiseks."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Paani <xliff:g id="TILE_NAME">%1$s</xliff:g> teisaldamine"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Teisaldage <xliff:g id="TILE_NAME">%1$s</xliff:g> asendisse <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Teenuse <xliff:g id="ID_1">%1$s</xliff:g> märguanne: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Rakendus ei toeta jagatud ekraani."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Rakendus ei toeta teisestel ekraanidel käivitamist."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ava seaded."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ava kiirseaded."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Sule kiirseaded."</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 1d5ac1a..8a37236 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -552,7 +552,7 @@
     <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta zaude eta hark sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
     <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> eta <xliff:g id="VPN_APP_1">%2$s</xliff:g> aplikazioetara konektatuta zaude, eta haiek sareko jarduerak gainbegira ditzakete, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
     <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora dago konektatuta laneko profila, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
-    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta duzu profil pertsonala, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta daukazu profil pertsonala, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
     <string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> aplikazioak kudeatzen du gailu hau."</string>
     <string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> erakundeak <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> erabiltzen du gailua kudeatzeko."</string>
     <string name="monitoring_description_do_body" msgid="7700878065625769970">"Gailuko ezarpenak, enpresa-sarbidea, aplikazioak eta datuak gainbegira eta kudea ditzake administratzaileak, baita gailuaren kokapen-informazioa ere."</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Erakutsi lehentasun txikiko jakinarazpenen ikonoak"</string>
     <string name="other" msgid="429768510980739978">"Beste bat"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Pantaila-zatitzailea"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Ezarri ezkerraldea pantaila osoan"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Ezarri ezkerraldea % 70en"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Ezarri ezkerraldea % 50en"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Ezarri ezkerraldea % 30en"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Ezarri eskuinaldea pantaila osoan"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Ezarri goialdea pantaila osoan"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Ezarri goialdea % 70en"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Ezarri goialdea % 50en"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Ezarri goialdea % 30en"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ezarri behealdea pantaila osoan"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. posizioa, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Editatzeko, sakatu birritan."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gehitzeko, sakatu birritan."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mugitu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Eraman <xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>garren postura"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikazioak ez du onartzen pantaila zatitua"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikazioa ezin da abiarazi bigarren mailako pantailatan."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ireki ezarpen bizkorrak."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Itxi ezarpen bizkorrak."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 55d8ab2..7f85959 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -808,7 +808,7 @@
     <string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"برگشت"</string>
     <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"اعلان‌ها"</string>
     <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"میان‌برهای صفحه‌کلید"</string>
-    <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر طرح‌بندی صفحه‌کلید"</string>
+    <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"تغییر جانمایی صفحه‌کلید"</string>
     <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"برنامه‌ها"</string>
     <string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"دستیار"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"مرورگر"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"نمایش نمادهای اعلان کم‌اهمیت"</string>
     <string name="other" msgid="429768510980739978">"موارد دیگر"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"تقسیم‌کننده صفحه"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"تمام‌صفحه چپ"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"٪۷۰ چپ"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"٪۵۰ چپ"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"٪۳۰ چپ"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"تمام‌صفحه راست"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"تمام‌صفحه بالا"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"٪۷۰ بالا"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"٪۵۰ بالا"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"٪۳۰ بالا"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"تمام‌صفحه پایین"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>. برای ویرایش دو ضربه سریع بزنید."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. برای افزودن دو ضربه سریع بزنید."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"انتقال <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"انتقال <xliff:g id="TILE_NAME">%1$s</xliff:g> به موقعیت <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"اعلان <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"ممکن است برنامه با تقسیم صفحه کار نکند."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"برنامه از تقسیم صفحه پشتیبانی نمی‌کند."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"برنامه از راه‌اندازی در نمایشگرهای ثانویه پشتیبانی نمی‌کند."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"باز کردن تنظیمات."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"باز کردن تنظیمات سریع."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"بستن تنظیمات سریع."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index f30c44b..fd9bbd9 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Näytä vähemmän tärkeät ilmoituskuvakkeet"</string>
     <string name="other" msgid="429768510980739978">"Muu"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Näytön jakaja"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Vasen koko näytölle"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Vasen 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Vasen 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Vasen 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Oikea koko näytölle"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Yläosa koko näytölle"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Yläosa 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Yläosa 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Yläosa 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Alaosa koko näytölle"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Muokkaa kaksoisnapauttamalla."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lisää kaksoisnapauttamalla."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g>."</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g> kohtaan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ilmoitus kohteesta <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Sovellus ei ehkä toimi jaetulla näytöllä."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Sovellus ei tue jaetun näytön tilaa."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Sovellus ei tue käynnistämistä toissijaisilla näytöillä."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Avaa asetukset."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Avaa pika-asetukset."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Sulje pika-asetukset."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index f6196dd63..8817ceb 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification de faible priorité"</string>
     <string name="other" msgid="429768510980739978">"Autre"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Séparateur d\'écran partagé"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Plein écran à la gauche"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70 % à la gauche"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50 % à la gauche"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30 % à la gauche"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Plein écran à la droite"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Plein écran dans le haut"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70 % dans le haut"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % dans le haut"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % dans le haut"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Plein écran dans le bas"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Touchez deux fois pour modifier."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Touchez deux fois pour ajouter."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Déplacer <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Déplacer <xliff:g id="TILE_NAME">%1$s</xliff:g> à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ouvrir les réglages rapides."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fermer les réglages rapides."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 0146ce4..35176c7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification à faible priorité"</string>
     <string name="other" msgid="429768510980739978">"Autre"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Séparateur d\'écran partagé"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Écran de gauche en plein écran"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Écran de gauche à 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Écran de gauche à 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Écran de gauche à 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Écran de droite en plein écran"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Écran du haut en plein écran"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Écran du haut à 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Écran du haut à 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Écran du haut à 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Écran du bas en plein écran"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, \"<xliff:g id="TILE_NAME">%2$s</xliff:g>\". Appuyer deux fois pour modifier."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Appuyer deux fois pour ajouter."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Déplacer \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Déplacer l\'élément \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de configuration rapide."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Application incompatible avec l\'écran partagé."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ouvrir la fenêtre de configuration rapide."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fermer la fenêtre de configuration rapide."</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index fd4d3c6..7c62798 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconas das notificacións que teñan baixa prioridade"</string>
     <string name="other" msgid="429768510980739978">"Outros"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Divisor de pantalla dividida"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Pantalla completa á esquerda"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70 % á esquerda"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50 % á esquerda"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30 % á esquerda"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Pantalla completa á dereita"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Pantalla completa arriba"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70 % arriba"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50 % arriba"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30 % arriba"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pantalla completa abaixo"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toca dúas veces o elemento para editalo."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dúas veces o elemento para engadilo"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" á posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Pode que a aplicación non funcione coa pantalla dividida."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"A aplicación non é compatible coa función de pantalla dividida."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"A aplicación non se pode iniciar en pantallas secundarias."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configuración."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Abrir configuración rápida."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Pechar a configuración rápida."</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 46f7a2b..af0d7d3 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"ઓછી પ્રાધાન્યતાનું નોટિફિકેશન આઇકન બતાવો"</string>
     <string name="other" msgid="429768510980739978">"અન્ય"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"સ્પ્લિટ-સ્ક્રીન વિભાજક"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ડાબી પૂર્ણ સ્ક્રીન"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ડાબે 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ડાબે 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ડાબે 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"જમણી સ્ક્રીન સ્ક્રીન"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"શીર્ષ પૂર્ણ સ્ક્રીન"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"શીર્ષ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"શીર્ષ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"શીર્ષ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"તળિયાની પૂર્ણ સ્ક્રીન"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"સ્થિતિ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. સંપાદિત કરવા માટે બે વાર ટૅપ કરો."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ઉમેરવા માટે બે વાર ટૅપ કરો."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ખસેડો"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> જગ્યા પર <xliff:g id="TILE_NAME">%1$s</xliff:g>ને ખસેડો"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ્સ સંપાદક."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> નોટિફિકેશન: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"વિભાજિત-સ્ક્રીન સાથે ઍપ્લિકેશન કદાચ કામ ન કરે."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર લૉન્ચનું સમર્થન કરતી નથી."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"સેટિંગ્સ ખોલો."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ઝડપી સેટિંગ્સ ખોલો."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ઝડપી સેટિંગ્સ બંધ કરો."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e4a6ed7..0a7515d 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -881,17 +881,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकता वाली सूचना के आइकॉन दिखाएं"</string>
     <string name="other" msgid="429768510980739978">"अन्य"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"विभाजित स्क्रीन विभाजक"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"बाईं स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"बाईं स्क्रीन को 70% बनाएं"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"बाईं स्क्रीन को 50% बनाएं"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"बाईं स्क्रीन को 30% बनाएं"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"दाईं स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ऊपर की स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ऊपर की स्क्रीन को 70% बनाएं"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ऊपर की स्क्रीन को 50% बनाएं"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ऊपर की स्क्रीन को 30% बनाएं"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"नीचे की स्क्रीन को फ़ुल स्क्रीन बनाएं"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. में बदलाव करने के लिए दो बार छूएं."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. जोड़ने के लिए दो बार छूएं."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> को ले जाएं"</string>
@@ -900,10 +889,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काे क्रम संख्या <xliff:g id="POSITION">%2$d</xliff:g> पर ले जाएं"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"हो सकता है कि ऐप्लिकेशन विभाजित स्क्रीन के साथ काम ना करे."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ऐप विभाजित स्‍क्रीन का समर्थन नहीं करता है."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर ऐप लॉन्च नहीं किया जा सकता."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग खोलें."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"क्विक सेटिंग खोलें."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"त्वरित सेटिंग बंद करें."</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index a27b066..27b6909 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -884,17 +884,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavijesti niskog prioriteta"</string>
     <string name="other" msgid="429768510980739978">"Ostalo"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Razdjelnik podijeljenog zaslona"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Lijevi zaslon u cijeli zaslon"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Lijevi zaslon na 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Lijevi zaslon na 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Lijevi zaslon na 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Desni zaslon u cijeli zaslon"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Gornji zaslon u cijeli zaslon"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Gornji zaslon na 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gornji zaslon na 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gornji zaslon na 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Donji zaslon u cijeli zaslon"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dodirnite dvaput da biste uredili."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dodirnite dvaput da biste dodali."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premjesti <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -903,10 +892,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premjestite pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavijest: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikacija ne podržava podijeljeni zaslon."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikacija ne podržava pokretanje na sekundarnim zaslonima."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvaranje postavki."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Otvaranje brzih postavki."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zatvaranje brzih postavki."</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 0d1f50a..1f52b2c 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -428,7 +428,7 @@
     <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Az NFC ki van kapcsolva"</string>
     <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Az NFC be van kapcsolva"</string>
     <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Képernyő rögzítése"</string>
-    <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Kezdés"</string>
+    <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Indítás"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Leállítás"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"Eszköz"</string>
     <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Váltás az alkalmazások között felfelé csúsztatással"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Alacsony prioritású értesítési ikonok mutatása"</string>
     <string name="other" msgid="429768510980739978">"Egyéb"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Elválasztó az osztott nézetben"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Bal oldali teljes képernyőre"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Bal oldali 70%-ra"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Bal oldali 50%-ra"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Bal oldali 30%-ra"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Jobb oldali teljes képernyőre"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Felső teljes képernyőre"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Felső 70%-ra"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Felső 50%-ra"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Felső 30%-ra"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Alsó teljes képernyőre"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozíció: <xliff:g id="TILE_NAME">%2$s</xliff:g>. Koppintson duplán a szerkesztéshez."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Koppintson duplán a hozzáadáshoz."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezése"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezése a következő pozícióba: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-értesítések: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Az alkalmazást nem lehet másodlagos kijelzőn elindítani."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Beállítások megnyitása."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Gyorsbeállítások megnyitása."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Gyorsbeállítások bezárása"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index f9a10fa..d4a093f 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
     <string name="other" msgid="429768510980739978">"Այլ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Տրոհված էկրանի բաժանիչ"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Ձախ էկրանը՝ լիաէկրան"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Ձախ էկրանը՝ 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Ձախ էկրանը՝ 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Ձախ էկրանը՝ 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Աջ էկրանը՝ լիաէկրան"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Վերևի էկրանը՝ լիաէկրան"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Վերևի էկրանը՝ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Վերևի էկրանը՝ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Վերևի էկրանը՝ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ներքևի էկրանը՝ լիաէկրան"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>: Կրկնակի հպեք՝ փոխելու համար:"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>: Կրկնակի հպեք՝ ավելացնելու համար:"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Տեղափոխել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը տեղափոխել դիրք <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ծանուցում՝ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում:"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Հավելվածը չի աջակցում գործարկումը լրացուցիչ էկրանների վրա"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Բացել կարգավորումները:"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Բացել արագ կարգավորումները:"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Փակել արագ կարգավորումները:"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f65be90..3b89b3d 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Tampilkan ikon notifikasi prioritas rendah"</string>
     <string name="other" msgid="429768510980739978">"Lainnya"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Pembagi layar terpisah"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Layar penuh di kiri"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Kiri 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Kiri 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Kiri 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Layar penuh di kanan"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Layar penuh di atas"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Atas 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Atas 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Atas 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Layar penuh di bawah"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Ketuk dua kali untuk mengedit."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ketuk dua kali untuk menambahkan."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pindahkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Pindahkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke posisi <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifikasi <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"App tidak mendukung layar terpisah."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikasi tidak mendukung peluncuran pada layar sekunder."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka setelan."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Buka setelan cepat."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Tutup setelan cepat."</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 66c9147..da8a092 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
     <string name="other" msgid="429768510980739978">"Annað"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Skjáskipting"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Vinstri á öllum skjánum"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Vinstri 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Vinstri 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Vinstri 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Hægri á öllum skjánum"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Efri á öllum skjánum"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Efri 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Efri 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Efri 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Neðri á öllum skjánum"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Staða <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Ýttu tvisvar til að breyta."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ýttu tvisvar til að bæta við."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Færa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Færa <xliff:g id="TILE_NAME">%1$s</xliff:g> í stöðu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> tilkynning: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Hugsanlega virkar forritið ekki ef skjánum er skipt upp."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Forritið styður ekki að skjánum sé skipt."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Forrit styður ekki opnun á öðrum skjá."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Opna stillingar."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Opna flýtistillingar."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Loka flýtistillingum."</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index b0e64a2..0ec9c23 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostra icone di notifiche con priorità bassa"</string>
     <string name="other" msgid="429768510980739978">"Altro"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Strumento per schermo diviso"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Schermata sinistra a schermo intero"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Schermata sinistra al 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Schermata sinistra al 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Schermata sinistra al 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Schermata destra a schermo intero"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Schermata superiore a schermo intero"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Schermata superiore al 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Schermata superiore al 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Schermata superiore al 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Schermata inferiore a schermo intero"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tocca due volte per modificare."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tocca due volte per aggiungere."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Sposta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Sposta il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> nella posizione <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifica di <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"L\'app potrebbe non funzionare con lo schermo diviso."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"L\'app non supporta la modalità Schermo diviso."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"L\'app potrebbe non funzionare su un display secondario."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"L\'app non supporta l\'avvio su display secondari."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Apri le impostazioni."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Apri le impostazioni rapide."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Chiudi le impostazioni rapide."</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 92fa09b..193c5f6 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
     <string name="other" msgid="429768510980739978">"אחר"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"מחלק מסך מפוצל"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"מסך שמאלי מלא"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"שמאלה 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"שמאלה 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"שמאלה 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"מסך ימני מלא"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"מסך עליון מלא"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"עליון 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"עליון 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"עליון 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"מסך תחתון מלא"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. הקש פעמיים כדי לערוך."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. הקש פעמיים כדי להוסיף."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"הזזת <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"העברת <xliff:g id="TILE_NAME">%1$s</xliff:g> למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"ייתכן שהיישום לא יפעל עם מסך מפוצל."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"האפליקציה אינה תומכת במסך מפוצל."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"האפליקציה אינה תומכת בהפעלה במסכים משניים."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"פתיחת הגדרות."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"פתיחה של \'הגדרות מהירות\'."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"סגירה של \'הגדרות מהירות\'."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index e427c8f..9aa9e76 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"優先度の低い通知アイコンを表示"</string>
     <string name="other" msgid="429768510980739978">"その他"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"分割画面の分割線"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"左全画面"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"左 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"左 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"左 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"右全画面"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"上部全画面"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"上 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"上 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"上 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"下部全画面"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> の <xliff:g id="TILE_NAME">%2$s</xliff:g> を編集するにはダブルタップします。"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を追加するにはダブルタップします。"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を移動します"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> をポジション <xliff:g id="POSITION">%2$d</xliff:g> に移動"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> の通知: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"アプリは分割画面では動作しないことがあります。"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"アプリで分割画面がサポートされていません。"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"アプリはセカンダリ ディスプレイでの起動に対応していません。"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"設定を開きます。"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"クイック設定を開きます。"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"クイック設定を閉じます。"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index b86bf8c..aa6f35d 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"დაბალი პრიორიტეტის მქონე შეტყობინებების ხატულების ჩვენება"</string>
     <string name="other" msgid="429768510980739978">"სხვა"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"გაყოფილი ეკრანის რეჟიმის გამყოფი"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"მარცხენა ნაწილის სრულ ეკრანზე გაშლა"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"მარცხენა ეკრანი — 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"მარცხენა ეკრანი — 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"მარცხენა ეკრანი — 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"მარჯვენა ნაწილის სრულ ეკრანზე გაშლა"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ზედა ნაწილის სრულ ეკრანზე გაშლა"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ზედა ეკრანი — 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ზედა ეკრანი — 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ზედა ეკრანი — 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ქვედა ნაწილის სრულ ეკრანზე გაშლა"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. რედაქტირებისთვის, შეეხეთ ორმაგად."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. დასამატებლად, შეეხეთ ორმაგად."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის გადატანა"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის გადატანა პოზიციაზე <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> შეტყობინება: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"აპს არ გააჩნია მეორეული ეკრანის მხარდაჭერა."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"პარამეტრების გახსნა."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"სწრაფი პარამეტრების გახსნა."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"სწრაფი პარამეტრების დახურვა."</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 1adaf2b..cafbc2b 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Маңызды емес хабарландыру белгішелерін көрсету"</string>
     <string name="other" msgid="429768510980739978">"Басқа"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Бөлінген экран бөлгіші"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Сол жағын толық экранға шығару"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70% сол жақта"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50% сол жақта"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30% сол жақта"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Оң жағын толық экранға шығару"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Жоғарғы жағын толық экранға шығару"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70% жоғарғы жақта"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% жоғарғы жақта"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% жоғарғы жақта"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Төменгісін толық экранға шығару"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> орны, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Өңдеу үшін екі рет түртіңіз."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Қосу үшін екі рет түртіңіз."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жылжыту"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> бөлшегін <xliff:g id="POSITION">%2$d</xliff:g>-позицияға жылжыту"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> хабарландыруы: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Қолданба бөлінген экранда жұмыс істемеуі мүмкін."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Қодланба бөлінген экранды қолдамайды."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Қолданба қосымша дисплейлерде іске қосуды қолдамайды."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Параметрлерді ашу."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Жылдам параметрлерді ашу."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Жылдам параметрлерді жабу."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 961a37b..5f16c76 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"បង្ហាញ​រូប​ការជូនដំណឹង​ដែលមានអាទិភាពទាប"</string>
     <string name="other" msgid="429768510980739978">"ផ្សេងៗ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"កម្មវិធីចែកអេក្រង់បំបែក"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"អេក្រង់ពេញខាងឆ្វេង"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ឆ្វេង 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ឆ្វេង 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ឆ្វេង 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"អេក្រង់ពេញខាងស្តាំ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"អេក្រង់ពេញខាងលើ"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ខាងលើ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ខាងលើ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ខាងលើ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"អេក្រង់ពេញខាងក្រោម"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ទីតាំង <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>, ប៉ះពីរដងដើម្បីកែ"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>, ប៉ះពីរដងដើម្បីបន្ថែម"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ផ្លាស់ទី <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ផ្លាស់ទី <xliff:g id="TILE_NAME">%1$s</xliff:g> ទៅទីតាំង <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ការជូនដំណឹង៖ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"កម្មវិធីអាចនឹងមិនដំណើរការនៅលើអេក្រង់បំបែកទេ"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"កម្មវិធីនេះ​ប្រហែល​ជាមិនដំណើរការ​នៅលើ​អេក្រង់បន្ទាប់បន្សំទេ។"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"កម្មវិធី​នេះមិន​អាច​ចាប់ផ្តើម​នៅលើ​អេក្រង់បន្ទាប់បន្សំបានទេ។"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"បើកការកំណត់"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"បើកការកំណត់រហ័ស"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"បិទការកំណត់រហ័ស"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 7ec6a9f..dff9ebd 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"ಕಡಿಮೆ-ಆದ್ಯತೆ ಸೂಚನೆಯ ಐಕಾನ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="other" msgid="429768510980739978">"ಇತರ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"ಸ್ಪ್ಲಿಟ್-ಪರದೆ ಡಿವೈಡರ್"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ಎಡ ಪೂರ್ಣ ಪರದೆ"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70% ಎಡಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50% ಎಡಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30% ಎಡಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"ಬಲ ಪೂರ್ಣ ಪರದೆ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ಮೇಲಿನ ಪೂರ್ಣ ಪರದೆ"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70% ಮೇಲಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% ಮೇಲಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% ಮೇಲಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ಸ್ಥಳ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ಎಡಿಟ್ ಮಾಡಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ಸೇರಿಸಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಸರಿಸಿ"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="POSITION">%2$d</xliff:g> ಗೆ ಸರಿಸಿ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳ ಎಡಿಟರ್."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"ವಿಭಜಿಸಿದ ಪರದೆಯಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ಮುಚ್ಚಿ."</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 840a98e..181888f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"우선순위가 낮은 알림 아이콘 표시"</string>
     <string name="other" msgid="429768510980739978">"기타"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"화면 분할기"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"왼쪽 화면 전체화면"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"왼쪽 화면 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"왼쪽 화면 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"왼쪽 화면 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"오른쪽 화면 전체화면"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"위쪽 화면 전체화면"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"위쪽 화면 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"위쪽 화면 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"위쪽 화면 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"아래쪽 화면 전체화면"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"위치 <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. 수정하려면 두 번 탭하세요."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. 추가하려면 두 번 탭하세요."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 이동"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일을 위치 <xliff:g id="POSITION">%2$d</xliff:g>(으)로 이동"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 알림: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"앱이 화면 분할을 지원하지 않습니다."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"앱이 보조 디스플레이에서의 실행을 지원하지 않습니다."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"설정 열기"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"빠른 설정 열기"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"빠른 설정 닫기"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 14ee566..47fc3a8 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
     <string name="other" msgid="429768510980739978">"Башка"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Экранды бөлгүч"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Сол жактагы экранды толук экран режимине өткөрүү"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Сол жактагы экранды 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Сол жактагы экранды 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Сол жактагы экранды 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Оң жактагы экранды толук экран режимине өткөрүү"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Үстүнкү экранды толук экран режимине өткөрүү"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Үстүнкү экранды 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Үстүнкү экранды 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Үстүнкү экранды 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ылдыйкы экранды толук экран режимине өткөрүү"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Орду - <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Түзөтүү үчүн эки жолу таптаңыз."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Кошуу үчүн эки жолу таптаңыз."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> дегенди жылдыруу"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> плиткасы <xliff:g id="POSITION">%2$d</xliff:g>-позицияга кошулсун"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам жөндөөлөр түзөткүчү."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> эскертмеси: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Колдонмодо экран бөлүнбөшү мүмкүн."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Колдонмодо экран бөлүнбөйт."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Колдонмону кошумча экрандарда иштетүүгө болбойт."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Жөндөөлөрдү ачуу."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ыкчам жөндөөлөрдү ачуу."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Ыкчам жөндөөлөрдү жабуу."</string>
diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml
index 90fc652..8237919 100644
--- a/packages/SystemUI/res/values-land-television/dimens.xml
+++ b/packages/SystemUI/res/values-land-television/dimens.xml
@@ -15,8 +15,9 @@
 -->
 
 <resources>
-  <!-- Width of volume bar -->
-  <dimen name="volume_dialog_row_width">252dp</dimen>
+  <!-- Height of volume bar -->
+  <dimen name="volume_dialog_row_height">200dp</dimen>
+  <dimen name="volume_dialog_panel_transparent_padding">17dp</dimen>
   <dimen name="tv_volume_dialog_bubble_size">36dp</dimen>
   <dimen name="tv_volume_dialog_corner_radius">40dp</dimen>
   <dimen name="tv_volume_dialog_row_padding">5dp</dimen>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 9e1b66f..b584dfe 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -47,4 +47,15 @@
 
     <dimen name="global_actions_power_dialog_item_height">130dp</dimen>
     <dimen name="global_actions_power_dialog_item_bottom_margin">35dp</dimen>
+
+    <dimen name="controls_management_top_padding">12dp</dimen>
+    <dimen name="controls_management_titles_margin">8dp</dimen>
+    <dimen name="controls_management_indicator_top_margin">8dp</dimen>
+    <dimen name="controls_management_list_margin">4dp</dimen>
+    <dimen name="controls_management_footer_height">56dp</dimen>
+    <dimen name="controls_management_zone_top_margin">24dp</dimen>
+
+    <!-- (footer_height -48dp)/2 -->
+    <dimen name="controls_management_footer_top_margin">4dp</dimen>
+    <dimen name="controls_management_favorites_top_margin">8dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 553d0da..cb87555 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"ສະແດງໄອຄອນການແຈ້ງເຕືອນຄວາມສຳຄັນຕ່ຳ"</string>
     <string name="other" msgid="429768510980739978">"ອື່ນໆ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"ຕົວຂັ້ນການແບ່ງໜ້າຈໍ"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ເຕັມໜ້າຈໍຊ້າຍ"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ຊ້າຍ 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ຊ້າຍ 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ຊ້າຍ 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"ເຕັມໜ້າຈໍຂວາ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ເຕັມໜ້າຈໍເທິງສຸດ"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ເທິງສຸດ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ເທິງສຸດ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ເທິງສຸດ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ເຕັມໜ້າຈໍລຸ່ມສຸດ"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ແຕະສອງເທື່ອເພື່ອແກ້ໄຂ."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ແຕະສອງເທື່ອເພື່ອເພີ່ມ."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ຍ້າຍ <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ຍ້າຍ <xliff:g id="TILE_NAME">%1$s</xliff:g> ໄປຕຳແໜ່ງ <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"ການແຈ້ງເຕືອນ <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ແອັບບໍ່ຮອງຮັບການເປີດໃນໜ້າຈໍທີສອງ."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ເປີດການຕັ້ງຄ່າ."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ເປີດການຕັ້ງຄ່າດ່ວນ."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ປິດການຕັ້ງຄ່າດ່ວນ."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 28d6e6b..ac90c0f 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Rodyti mažo prioriteto pranešimų piktogramas"</string>
     <string name="other" msgid="429768510980739978">"Kita"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Skaidyto ekrano daliklis"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Kairysis ekranas viso ekrano režimu"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Kairysis ekranas 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Kairysis ekranas 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Kairysis ekranas 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Dešinysis ekranas viso ekrano režimu"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Viršutinis ekranas viso ekrano režimu"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Viršutinis ekranas 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Viršutinis ekranas 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Viršutinis ekranas 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Apatinis ekranas viso ekrano režimu"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> padėtis, išklotinės elementas „<xliff:g id="TILE_NAME">%2$s</xliff:g>“. Dukart palieskite, kad redaguotumėte."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Išklotinės elementas „<xliff:g id="TILE_NAME">%1$s</xliff:g>“. Dukart palieskite, kad pridėtumėte."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Perkelti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Perkelti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ į <xliff:g id="POSITION">%2$d</xliff:g> padėtį"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"„<xliff:g id="ID_1">%1$s</xliff:g>“ pranešimas: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Programa gali neveikti naudojant skaidytą ekraną."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Programoje nepalaikomas skaidytas ekranas."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Programa gali neveikti antriniame ekrane."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Programa nepalaiko paleisties antriniuose ekranuose."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atidaryti nustatymus."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Atidaryti sparčiuosius nustatymus."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Uždaryti sparčiuosius nustatymus."</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 223a57f..2f5edd3 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -884,17 +884,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Rādīt zemas prioritātes paziņojumu ikonas"</string>
     <string name="other" msgid="429768510980739978">"Citi"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Ekrāna sadalītājs"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Kreisā daļa pa visu ekrānu"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Pa kreisi 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Pa kreisi 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Pa kreisi 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Labā daļa pa visu ekrānu"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Augšdaļa pa visu ekrānu"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Augšdaļa 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Augšdaļa 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Augšdaļa 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Apakšdaļu pa visu ekrānu"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozīcija, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Lai rediģētu, veiciet dubultskārienu."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lai pievienotu, veiciet dubultskārienu."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pārvietot elementu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -903,10 +892,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Pārvietot elementu “<xliff:g id="TILE_NAME">%1$s</xliff:g>” uz <xliff:g id="POSITION">%2$d</xliff:g>. pozīciju"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> paziņojums: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Iespējams, lietotnē nedarbosies ekrāna sadalīšana."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Lietotnē netiek atbalstīta palaišana sekundārajos displejos."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atvērt iestatījumus."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Atvērt ātros iestatījumus."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Aizvērt ātros iestatījumus."</string>
diff --git a/packages/SystemUI/res/values-mcc310-mnc004/strings.xml b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml
new file mode 100644
index 0000000..f8ed0c0
--- /dev/null
+++ b/packages/SystemUI/res/values-mcc310-mnc004/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2020, 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SystemUI/res/values-mcc311-mnc480/strings.xml b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml
new file mode 100644
index 0000000..f8ed0c0
--- /dev/null
+++ b/packages/SystemUI/res/values-mcc311-mnc480/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2020, 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Content description of the data connection type 5G UW. [CHAR LIMIT=NONE] -->
+    <string name="data_connection_5g_plus" translatable="false">5G UW</string>
+</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a591031..de9cf0f 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Прикажувај икони за известувања со низок приоритет"</string>
     <string name="other" msgid="429768510980739978">"Друго"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Разделник на поделен екран"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Левиот на цел екран"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Левиот 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Левиот 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Левиот 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Десниот на цел екран"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Горниот на цел екран"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Горниот 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Горниот 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Горниот 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Долниот на цел екран"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Место <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Допрете двапати за уредување."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Допрете двапати за додавање."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Преместете <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместете <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицијата <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известување од <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Апликацијата можеби нема да работи во поделен екран."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Апликацијата не поддржува поделен екран."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Апликацијата може да не функционира на друг екран."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Апликацијата не поддржува стартување на други екрани."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отворете ги поставките."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Отворете ги брзите поставки."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Затворете ги брзите поставки."</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 47d220d..bd51874 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"പ്രാധാന്യം കുറഞ്ഞ അറിയിപ്പ് ചിഹ്‌നങ്ങൾ"</string>
     <string name="other" msgid="429768510980739978">"മറ്റുള്ളവ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"സ്പ്ലിറ്റ്-സ്ക്രീൻ ഡിവൈഡർ"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ഇടത് പൂർണ്ണ സ്ക്രീൻ"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ഇടത് 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ഇടത് 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ഇടത് 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"വലത് പൂർണ്ണ സ്ക്രീൻ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"മുകളിൽ പൂർണ്ണ സ്ക്രീൻ"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"മുകളിൽ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"മുകളിൽ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"മുകളിൽ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"താഴെ പൂർണ്ണ സ്ക്രീൻ"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. എഡിറ്റുചെയ്യുന്നതിന് രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ചേർക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കുക"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> സ്ഥാനത്തേയ്ക്ക് <xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കുക"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"സ്പ്ലിറ്റ്-സ്ക്രീനിനൊപ്പം ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"രണ്ടാം ഡിസ്‌പ്ലേകളിൽ സമാരംഭിക്കുന്നതിനെ ആപ്പ് അനുവദിക്കുന്നില്ല."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ക്രമീകരണം തുറക്കുക."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ദ്രുത ക്രമീകരണം തുറക്കുക."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ദ്രുത ക്രമീകരണം അടയ്ക്കുക."</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 202f292..1440103 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string>
     <string name="other" msgid="429768510980739978">"Бусад"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"\"Дэлгэц хуваах\" хуваагч"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Зүүн талын бүтэн дэлгэц"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Зүүн 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Зүүн 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Зүүн 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Баруун талын бүтэн дэлгэц"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Дээд талын бүтэн дэлгэц"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Дээд 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Дээд 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Дээд 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Доод бүтэн дэлгэц"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Байршил <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Засахын тулд 2 удаа дарна уу."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Нэмэхийн тулд 2 удаа дарна уу."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г зөөх"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г байрлал <xliff:g id="POSITION">%2$d</xliff:g> руу зөөх"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Түргэн тохиргоо засварлагч."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> мэдэгдэл: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Апп хуваагдсан дэлгэцэд ажиллахгүй."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Аппыг хоёрдогч дэлгэцэд эхлүүлэх боломжгүй."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Тохиргоог нээнэ үү."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Шуурхай тохиргоог нээнэ үү."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Хурдан тохиргоог хаана уу."</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index e412072..32fe7bd 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"कमी प्राधान्य सूचना आयकन दर्शवा"</string>
     <string name="other" msgid="429768510980739978">"अन्य"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"विभाजित-स्क्रीन विभाजक"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"डावी फुल स्क्रीन"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"डावी 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"डावी 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"डावी 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"उजवी फुल स्क्रीन"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"शीर्ष फुल स्क्रीन"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"शीर्ष 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"शीर्ष 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"शीर्ष 10"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"तळाशी फुल स्क्रीन"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिती <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. संपादित करण्यासाठी दोनदा टॅप करा."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> . जोडण्यासाठी दोनदा टॅप करा."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> हलवा"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर हलवा"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"अ‍ॅप कदाचित विभाजित-स्क्रीनसह कार्य करू शकत नाही."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"दुसऱ्या डिस्प्लेवर अ‍ॅप लाँच होणार नाही."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग्ज उघडा."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"जलद सेटिंग्ज उघडा."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"जलद सेटिंग्ज बंद करा."</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index d01ab8c..fe246d1 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -403,7 +403,7 @@
       <item quantity="one">%d peranti</item>
     </plurals>
     <string name="quick_settings_notifications_label" msgid="3379631363952582758">"Pemberitahuan"</string>
-    <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampu suluh"</string>
+    <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampu Suluh"</string>
     <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera sedang digunakan"</string>
     <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Data mudah alih"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Penggunaan data"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Tunjukkan ikon pemberitahuan keutamaan rendah"</string>
     <string name="other" msgid="429768510980739978">"Lain-lain"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Pembahagi skrin pisah"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Skrin penuh kiri"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Kiri 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Kiri 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Kiri 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Skrin penuh kanan"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Skrin penuh atas"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Atas 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Atas 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Atas 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Skrin penuh bawah"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dwiketik untuk mengedit."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dwiketik untuk menambah."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Alihkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Alihkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke kedudukan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Pemberitahuan <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Apl mungkin tidak berfungsi dengan skrin pisah."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Apl tidak menyokong skrin pisah."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Apl tidak menyokong pelancaran pada paparan kedua."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka tetapan."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Buka tetapan pantas."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Tutup tetapan pantas."</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 83272f2..53c02a5 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"အရေးမကြီးသော အကြောင်းကြားချက် သင်္ကေတများ ပြရန်"</string>
     <string name="other" msgid="429768510980739978">"အခြား"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"မျက်နှာပြင်ခွဲခြမ်း ပိုင်းခြားပေးသည့်စနစ်"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ဘယ်ဘက် မျက်နှာပြင်အပြည့်"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ဘယ်ဘက်မျက်နှာပြင် ၇၀%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ဘယ်ဘက် မျက်နှာပြင် ၅၀%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ဘယ်ဘက် မျက်နှာပြင် ၃၀%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"ညာဘက် မျက်နှာပြင်အပြည့်"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"အပေါ်ဘက် မျက်နှာပြင်အပြည့်"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"အပေါ်ဘက် မျက်နှာပြင် ၇၀%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"အပေါ်ဘက် မျက်နှာပြင် ၅၀%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"အပေါ်ဘက် မျက်နှာပြင် ၃၀%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"အောက်ခြေ မျက်နှာပြင်အပြည့်"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>၊ <xliff:g id="TILE_NAME">%2$s</xliff:g> နေရာ။ တည်းဖြတ်ရန် နှစ်ချက်တို့ပါ။"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>။ ပေါင်းထည့်ရန် နှစ်ချက်တို့ပါ။"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုရွှေ့ပါ"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကို အနေအထား <xliff:g id="POSITION">%2$d</xliff:g> သို့ ရွှေ့ရန်"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> အကြောင်းကြားချက် − <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"မျက်နှာပြင် ခွဲခြမ်းပြသမှုဖြင့် အက်ပ်သည် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"အက်ပ်သည် မျက်နှာပြင်ခွဲပြရန် ပံ့ပိုးထားခြင်းမရှိပါ။"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ဤအက်ပ်အနေဖြင့် ဖွင့်ရန်စနစ်ကို ဒုတိယဖန်သားပြင်မှ အသုံးပြုရန် ပံ့ပိုးမထားပါ။"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ဆက်တင်များကို ဖွင့်ပါ။"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"အမြန်ဆက်တင်များကို ဖွင့်ပါ။"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"အမြန်ဆက်တင်များကို ပိတ်ပါ။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 01859ef..5c9f63f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for varsler med lav prioritet"</string>
     <string name="other" msgid="429768510980739978">"Annet"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Skilleelement for delt skjerm"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Utvid den venstre delen av skjermen til hele skjermen"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Sett størrelsen på den venstre delen av skjermen til 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Sett størrelsen på den venstre delen av skjermen til 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Sett størrelsen på den venstre delen av skjermen til 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Utvid den høyre delen av skjermen til hele skjermen"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Utvid den øverste delen av skjermen til hele skjermen"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Sett størrelsen på den øverste delen av skjermen til 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Sett størrelsen på den øverste delen av skjermen til 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Sett størrelsen på den øverste delen av skjermen til 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Utvid den nederste delen av skjermen til hele skjermen"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Plassering <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dobbelttrykk for å endre."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dobbelttrykk for å legge til."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flytt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flytt <xliff:g id="TILE_NAME">%1$s</xliff:g> til posisjon <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-varsel: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Det kan hende at appen ikke fungerer med delt skjerm."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Appen støtter ikke delt skjerm."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Appen kan ikke kjøres på sekundære skjermer."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åpne innstillingene."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Åpner hurtiginnstillingene."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Lukk hurtiginnstillingene."</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e548a96..791e4d3 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -500,8 +500,8 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"ब्याट्री सेभर अन छ"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"प्रदर्शन र पृष्ठभूमि डेटा घटाउँनुहोस्"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ब्याट्री सेभर अफ गर्नुहोस्"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
-    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"यो कार्य प्रदान गर्ने सेवाले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, फोटो, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
+    <string name="media_projection_dialog_service_text" msgid="958000992162214611">"यो कार्य प्रदान गर्ने सेवाले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, फोटो, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मार्फत रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"फेरि नदेखाउनुहोस्"</string>
@@ -716,7 +716,7 @@
     <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फोनको सेटिङका आधारमा घन्टी बज्न वा कम्पन हुन सक्छ। <xliff:g id="APP_NAME">%1$s</xliff:g> का वार्तालापहरू पूर्वनिर्धारित रूपमा बबलमा देखाइन्छन्।"</string>
     <string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ्लोटिङ सर्टकटमार्फत यो सामग्रीतर्फ तपाईंको ध्यान आकर्षित गर्दछ।"</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टमलाई यो सूचना आउँदा ध्वनि बज्नु पर्छ वा कम्पन हुनु पर्छ भन्ने कुराको निधो गर्न दिनुहोस्"</string>
-    <string name="notification_channel_summary_priority" msgid="7952654515769021553">"वार्तालाप खण्डको सिरानमा देखा पर्छ, तैरने बबलका रूपमा देखा पर्छ, लक स्क्रिनमा प्रोफाइल तस्बिर देखाइन्छ"</string>
+    <string name="notification_channel_summary_priority" msgid="7952654515769021553">"वार्तालाप खण्डको सिरानमा देखा पर्छ, तैरने बबलका रूपमा देखा पर्छ, लक स्क्रिनमा प्रोफाइल फोटो देखाइन्छ"</string>
     <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिङ"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> मा वार्तालापसम्बन्धी सुविधा प्रयोग गर्न मिल्दैन"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकताका सूचना आइकनहरू देखाउनुहोस्"</string>
     <string name="other" msgid="429768510980739978">"अन्य"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"विभाजित-स्क्रिन छुट्याउने"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"बायाँ भाग फुल स्क्रिन"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"बायाँ भाग ७०%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"बायाँ भाग ५०%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"बायाँ भाग ३०%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"दायाँ भाग फुल स्क्रिन"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"माथिल्लो भाग फुल स्क्रिन"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"माथिल्लो भाग ७०%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"माथिल्लो भाग ५०%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"माथिल्लो भाग ३०%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"तल्लो भाग फुल स्क्रिन"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। सम्पादन गर्नाका लागि डबल ट्याप गर्नुहोस्।"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। थप्नका लागि डबल ट्याप गर्नुहोस्।"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई सार्नुहोस्"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई <xliff:g id="POSITION">%2$d</xliff:g> स्थितिमा सार्नुहोस्"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> को सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"अनुप्रयोगले विभाजित-स्क्रिनमा काम नगर्न सक्छ।"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिङहरूलाई खोल्नुहोस्।"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"द्रुत सेटिङहरूलाई खोल्नुहोस्।"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"द्रुत सेटिङहरूलाई बन्द गर्नुहोस्।"</string>
@@ -1019,7 +1004,7 @@
     <string name="priority_onboarding_title" msgid="2893070698479227616">"वार्तालापको प्राथमिकता निर्धारण गरी \"महत्त्वपूर्ण\" बनाइयो"</string>
     <string name="priority_onboarding_behavior" msgid="5342816047020432929">"महत्वपूर्ण वार्तालापहरू:"</string>
     <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"वार्तालाप खण्डको सिरानमा देखिने छन्"</string>
-    <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"लक स्क्रिनमा प्रोफाइल तस्बिर देखाउने छन्"</string>
+    <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"लक स्क्रिनमा प्रोफाइल फोटो देखाउने छन्"</string>
     <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"एपहरूमाथि तैरिने बबलका रूपमा देखाइयोस्"</string>
     <string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"बाधा नपुऱ्याउनुहोस् मोडलाई बेवास्ता गरियोस्"</string>
     <string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"बुझेँ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index fa028c3..b02be5d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Pictogrammen voor meldingen met lage prioriteit weergeven"</string>
     <string name="other" msgid="429768510980739978">"Overig"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Scheiding voor gesplitst scherm"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Linkerscherm op volledig scherm"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Linkerscherm 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Linkerscherm 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Linkerscherm 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Rechterscherm op volledig scherm"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Bovenste scherm op volledig scherm"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Bovenste scherm 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Bovenste scherm 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Bovenste scherm 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Onderste scherm op volledig scherm"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Positie <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dubbeltik om te bewerken."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om toe te voegen."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verplaatsen"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verplaatsen naar positie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"App werkt mogelijk niet met gesplitst scherm."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"App biedt geen ondersteuning voor gesplitst scherm."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"App werkt mogelijk niet op een secundair scherm."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"App kan niet op secundaire displays worden gestart."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Instellingen openen."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Snelle instellingen openen."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Snelle instellingen sluiten."</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 4c6ef48..c7de8ca 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"କମ୍‍-ଅଗ୍ରାଧିକାର ବିଜ୍ଞପ୍ତି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ"</string>
     <string name="other" msgid="429768510980739978">"ଅନ୍ୟାନ୍ୟ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନ ବିଭାଜକ"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ବାମ ପଟକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍ କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ବାମ ପଟକୁ 70% କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ବାମ ପଟକୁ 50% କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ବାମ ପଟେ 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"ଡାହାଣ ପଟକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍ କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ଉପର ଆଡ଼କୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍ କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ଉପର ଆଡ଼କୁ 70% କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ଉପର ଆଡ଼କୁ 50% କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ଉପର ଆଡ଼କୁ 30% କରନ୍ତୁ"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ତଳ ଅଂଶର ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ଅବସ୍ଥାନ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। ଏଡିଟ୍ କରିବାକୁ ଡବଲ୍‍-ଟାପ୍‍ କରନ୍ତୁ।"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ଯୋଡ଼ିବା ପାଇଁ ଡବଲ୍‍-ଟାପ୍‍ କରନ୍ତୁ।"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ନିଅନ୍ତୁ"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> ଅବସ୍ଥାନକୁ <xliff:g id="TILE_NAME">%1$s</xliff:g> ଘୁଞ୍ଚାନ୍ତୁ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନରେ ଆପ୍‍ କାମ କରିନପାରେ।"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ଆପ୍‍ ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ ଲଞ୍ଚ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ସେଟିଂସ୍ ଖୋଲନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ଦ୍ରୁତ ସେଟିଂସ୍ ବନ୍ଦ କରନ୍ତୁ।"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 98583c0..66333b9 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੇ ਸੂਚਨਾ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਦਿਖਾਓ"</string>
     <string name="other" msgid="429768510980739978">"ਹੋਰ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਡਿਵਾਈਡਰ"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ਖੱਬੇ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ਖੱਬੇ 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ਖੱਬੇ 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ਖੱਬੇ 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"ਸੱਜੇ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ਉੱਪਰ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ਉੱਪਰ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ਉੱਪਰ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ਉੱਪਰ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"ਹੇਠਾਂ ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ਸਥਿਤੀ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। ਸੰਪਾਦਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ ਤਬਦੀਲ ਕਰੋ"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="POSITION">%2$d</xliff:g> ਸਥਾਨ \'ਤੇ ਲਿਜਾਓ"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ਸੂਚਨਾ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇਆਂ \'ਤੇ ਲਾਂਚ ਕਰਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਖੋਲ੍ਹੋ।"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬੰਦ ਕਰੋ।"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 4d5a2ae..c68e3ac 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Pokazuj ikony powiadomień o niskim priorytecie"</string>
     <string name="other" msgid="429768510980739978">"Inne"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Linia dzielenia ekranu"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Lewa część ekranu na pełnym ekranie"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70% lewej części ekranu"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50% lewej części ekranu"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30% lewej części ekranu"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Prawa część ekranu na pełnym ekranie"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Górna część ekranu na pełnym ekranie"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70% górnej części ekranu"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% górnej części ekranu"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% górnej części ekranu"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Dolna część ekranu na pełnym ekranie"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Położenie <xliff:g id="POSITION">%1$d</xliff:g>, kafelek <xliff:g id="TILE_NAME">%2$s</xliff:g>. Kliknij dwukrotnie, by edytować."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>. Kliknij dwukrotnie, by dodać."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Przenieś kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Przenieś kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> w położenie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Powiadomienie z aplikacji <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacja może nie działać przy podzielonym ekranie."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikacja nie obsługuje dzielonego ekranu."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikacja może nie działać na dodatkowym ekranie."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikacja nie obsługuje uruchamiania na dodatkowych ekranach."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otwórz ustawienia."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Otwórz szybkie ustawienia."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zamknij szybkie ustawienia."</string>
@@ -1016,7 +1001,7 @@
     <string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Przenieś w lewy dolny róg"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Przenieś w prawy dolny róg"</string>
     <string name="bubble_dismiss_text" msgid="1314082410868930066">"Zamknij dymek"</string>
-    <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nie wyświetlaj rozmowy jako dymku"</string>
+    <string name="bubbles_dont_bubble_conversation" msgid="1033040343437428822">"Nie wyświetlaj rozmowy jako dymka"</string>
     <string name="bubbles_user_education_title" msgid="5547017089271445797">"Czatuj, korzystając z dymków"</string>
     <string name="bubbles_user_education_description" msgid="1160281719576715211">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string>
     <string name="bubbles_user_education_manage_title" msgid="2848511858160342320">"Zarządzaj dymkami w dowolnym momencie"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index ef5c9ce..ff2ecd3 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
     <string name="other" msgid="429768510980739978">"Outros"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Divisor de tela"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Lado esquerdo em tela cheia"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Esquerda a 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Esquerda a 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Esquerda a 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Lado direito em tela cheia"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Parte superior em tela cheia"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Parte superior a 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Parte superior a 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Parte superior a 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Parte inferior em tela cheia"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"É possível que o app não funcione com o recurso de divisão de tela."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"O app não é compatível com a divisão de tela."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"É possível que o app não funcione em uma tela secundária."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"O app não é compatível com a inicialização em telas secundárias."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Abrir as configurações rápidas."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fechar as configurações rápidas."</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 541f12c..c39a5eb 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de prioridade baixa"</string>
     <string name="other" msgid="429768510980739978">"Outro"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Divisor do ecrã dividido"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Ecrã esquerdo inteiro"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"70% no ecrã esquerdo"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"50% no ecrã esquerdo"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"30% no ecrã esquerdo"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Ecrã direito inteiro"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Ecrã superior inteiro"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"70% no ecrã superior"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"50% no ecrã superior"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"30% no ecrã superior"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ecrã inferior inteiro"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"A app pode não funcionar com o ecrã dividido."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"A app não é compatível com o ecrã dividido."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"A app pode não funcionar num ecrã secundário."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"A app não é compatível com o início em ecrãs secundários."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir as definições."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Abrir as definições rápidas."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fechar as definições rápidas."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index ef5c9ce..ff2ecd3 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
     <string name="other" msgid="429768510980739978">"Outros"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Divisor de tela"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Lado esquerdo em tela cheia"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Esquerda a 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Esquerda a 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Esquerda a 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Lado direito em tela cheia"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Parte superior em tela cheia"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Parte superior a 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Parte superior a 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Parte superior a 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Parte inferior em tela cheia"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"É possível que o app não funcione com o recurso de divisão de tela."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"O app não é compatível com a divisão de tela."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"É possível que o app não funcione em uma tela secundária."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"O app não é compatível com a inicialização em telas secundárias."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Abrir as configurações rápidas."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fechar as configurações rápidas."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3e2373d..b793bcd 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -884,17 +884,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Afișați pictogramele de notificare cu prioritate redusă"</string>
     <string name="other" msgid="429768510980739978">"Altele"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Separator pentru ecranul împărțit"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Partea stângă pe ecran complet"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Partea stângă: 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Partea stângă: 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Partea stângă: 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Partea dreaptă pe ecran complet"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Partea de sus pe ecran complet"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Partea de sus: 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Partea de sus: 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Partea de sus: 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Partea de jos pe ecran complet"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Atingeți de două ori pentru a edita."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Atingeți de două ori pentru a adăuga."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mutați <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -903,10 +892,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mutați <xliff:g id="TILE_NAME">%1$s</xliff:g> pe poziția <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplicația nu acceptă ecranul împărțit."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplicația nu acceptă lansare pe ecrane secundare."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschideți setările."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Deschideți setările rapide."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Închideți setările rapide."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 7747724..7560f53 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
     <string name="other" msgid="429768510980739978">"Другое"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Разделитель экрана"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Левый во весь экран"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Левый на 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Левый на 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Левый на 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Правый во весь экран"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Верхний во весь экран"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Верхний на 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Верхний на 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Верхний на 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Нижний во весь экран"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>, кнопка \"<xliff:g id="TILE_NAME">%2$s</xliff:g>\". Чтобы изменить, нажмите дважды."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Кнопка \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\". Чтобы добавить, нажмите дважды."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Переместить кнопку \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Переместить значок <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицию <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Уведомление <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Приложение не поддерживает разделение экрана."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Приложение не поддерживает разделение экрана."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Приложение может не работать на дополнительном экране"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Приложение не поддерживает запуск на дополнительных экранах"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Открыть настройки."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Развернуть быстрые настройки."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Скрыть быстрые настройки."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index b8813f2..02f9c23 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"අඩු ප්‍රමුඛතා දැනුම්දීම් අයිකන පෙන්වන්න"</string>
     <string name="other" msgid="429768510980739978">"වෙනත්"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"බෙදුම්-තිර වෙන්කරණය"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"වම් පූර්ණ තිරය"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"වම් 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"වම් 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"වම් 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"දකුණු පූර්ණ තිරය"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ඉහළම පූර්ණ තිරය"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ඉහළම 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ඉහළම 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ඉහළම 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"පහළ පූර්ණ තිරය"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. වෙනස් කිරීමට දෙවරක් තට්ටු කරන්න."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. එක් කිරීමට දෙවරක් තට්ටු කරන්න."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ගෙන යන්න"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> තත්ත්වයට <xliff:g id="POSITION">%2$d</xliff:g> ගෙන යන්න"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> දැනුම්දීම: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"යෙදුම බෙදුම්-තිරය සමග ක්‍රියා නොකළ හැකිය."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"යෙදුම ද්විතියික සංදර්ශකයක ක්‍රියා නොකළ හැකිය."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"යෙදුම ද්විතීයික සංදර්ශක මත දියත් කිරීම සඳහා සහාය නොදක්වයි."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"සැකසීම් විවෘත කරන්න."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"ඉක්මන් සැකසීම් විවෘත කරන්න."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ඉක්මන් සැකසීම් වසන්න."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0f9cb5f..54a32dd 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Zobraziť ikony upozornení s nízkou prioritou"</string>
     <string name="other" msgid="429768510980739978">"Ďalšie"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Rozdeľovač obrazovky"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Ľavá – na celú obrazovku"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Ľavá – 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Ľavá – 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Ľavá – 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Pravá– na celú obrazovku"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Horná – na celú obrazovku"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Horná – 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Horná – 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Horná – 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Dolná – na celú obrazovku"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozícia <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Upravíte ju dvojitým klepnutím."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Pridáte ju dvojitým klepnutím."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Presunúť dlaždicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Presunúť <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozíciu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Upozornenie <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikácia nemusí fungovať so zapnutou rozdelenou obrazovkou."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikácia nepodporuje spúšťanie na sekundárnych obrazovkách."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvoriť nastavenia"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Otvoriť rýchle nastavenia"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zavrieť rýchle nastavenia"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 18db332..32fce7d 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Pokaži ikone obvestil z nizko stopnjo prednosti"</string>
     <string name="other" msgid="429768510980739978">"Drugo"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Razdelilnik zaslonov"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Levi v celozaslonski način"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Levi 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Levi 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Levi 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Desni v celozaslonski način"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Zgornji v celozaslonski način"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Zgornji 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Zgornji 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Zgornji 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Spodnji v celozaslonski način"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Če želite urediti, se dvakrat dotaknite."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Če želite dodati, se dvakrat dotaknite."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premik tega: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premakni ploščico <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obvestilo za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikacija ne podpira zagona na sekundarnih zaslonih."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Odpri nastavitve."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Odpri hitre nastavitve."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Zapri hitre nastavitve."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 57ab1c3..c907bc2 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -480,7 +480,7 @@
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string>
-    <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo!"</string>
+    <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo"</string>
     <string name="guest_notification_title" msgid="4434456703930764167">"Përdorues vizitor"</string>
     <string name="guest_notification_text" msgid="4202692942089571351">"Për të fshirë aplikacionet dhe të dhënat, hiqe përdoruesin vizitor"</string>
     <string name="guest_notification_remove_action" msgid="4153019027696868099">"HIQ VIZITORIN"</string>
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Shfaq ikonat e njoftimeve me përparësi të ulët"</string>
     <string name="other" msgid="429768510980739978">"Të tjera"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Ndarësi i ekranit të ndarë"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Ekrani i plotë majtas"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Majtas 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Majtas 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Majtas 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Ekrani i plotë djathtas"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Ekrani i plotë lart"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Lart 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Lart 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Lart 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ekrani i plotë poshtë"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Trokit dy herë për ta redaktuar."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Trokit dy herë për ta shtuar."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Zhvendose <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Zhvendos <xliff:g id="TILE_NAME">%1$s</xliff:g> te pozicioni <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Aplikacioni nuk mbështet nisjen në ekrane dytësore."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Hap cilësimet."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Hap cilësimet e shpejta."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Mbyll cilësimet e shpejta."</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index ec763f6..8b7ce1f 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -884,17 +884,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Прикажи иконе обавештења ниског приоритета"</string>
     <string name="other" msgid="429768510980739978">"Друго"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Разделник подељеног екрана"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Режим целог екрана за леви екран"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Леви екран 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Леви екран 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Леви екран 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Режим целог екрана за доњи екран"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Режим целог екрана за горњи екран"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Горњи екран 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Горњи екран 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Горњи екран 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Режим целог екрана за доњи екран"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Двапут додирните да бисте изменили."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двапут додирните да бисте додали."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Премести плочицу <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -903,10 +892,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместите „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позицију <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Обавештења за <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Апликација можда неће функционисати са подељеним екраном."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Апликација не подржава подељени екран."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Апликација можда неће функционисати на секундарном екрану."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Апликација не подржава покретање на секундарним екранима."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отвори Подешавања."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Отвори Брза подешавања."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Затвори Брза подешавања."</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index a0a563d..c4b7977 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Visa ikoner för aviseringar med låg prioritet"</string>
     <string name="other" msgid="429768510980739978">"Annat"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Avdelare för delad skärm"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Helskärm på vänster skärm"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Vänster 70 %"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Vänster 50 %"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Vänster 30 %"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Helskärm på höger skärm"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Helskärm på övre skärm"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Övre 70 %"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Övre 50 %"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Övre 30 %"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Helskärm på nedre skärm"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tryck snabbt två gånger för att redigera positionen."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lägg till genom att trycka snabbt två gånger."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flytta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flytta <xliff:g id="TILE_NAME">%1$s</xliff:g> till position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-avisering: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Appen kanske inte fungerar med delad skärm."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Appen har inte stöd för delad skärm."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Appen kanske inte fungerar på en sekundär skärm."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Appen kan inte köras på en sekundär skärm."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Öppna inställningarna."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Öppna snabbinställningarna."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Stäng snabbinställningarna"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 7e264a4..b483fa8 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Onyesha aikoni za arifa zisizo muhimu"</string>
     <string name="other" msgid="429768510980739978">"Nyingine"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Kitenganishi cha skrini inayogawanywa"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Skrini nzima ya kushoto"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Kushoto 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Kushoto 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Kushoto 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Skrini nzima ya kulia"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Skrini nzima ya juu"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Juu 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Juu 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Juu 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Skrini nzima ya chini"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gusa mara mbili ili ubadilishe."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gusa mara mbili ili uongeze."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Hamisha <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Sogeza <xliff:g id="TILE_NAME">%1$s</xliff:g> kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Arifa kutoka <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Huenda programu isifanye kazi kwenye skrini inayogawanywa."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Programu hii haiwezi kufunguliwa kwenye madirisha mengine."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Fungua mipangilio."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Fungua mipangilio ya haraka."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Funga mipangilio ya haraka."</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 5d20564..be66320 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -29,9 +29,6 @@
     <!-- Nav bar button default ordering/layout -->
     <string name="config_navBarLayout" translatable="false">left;back,home,recent;right</string>
 
-    <!-- Animation duration when using long press on recents to dock -->
-    <integer name="long_press_dock_anim_duration">290</integer>
-
     <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
     <integer name="navigation_bar_deadzone_orientation">0</integer>
 
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index e0f20ce..2c025e7 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"குறைந்த முன்னுரிமை உள்ள அறிவிப்பு ஐகான்களைக் காட்டு"</string>
     <string name="other" msgid="429768510980739978">"மற்றவை"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"திரையைப் பிரிக்கும் பிரிப்பான்"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"இடது புறம் முழுத் திரை"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"இடது புறம் 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"இடது புறம் 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"இடது புறம் 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"வலது புறம் முழுத் திரை"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"மேற்புறம் முழுத் திரை"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"மேலே 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"மேலே 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"மேலே 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"கீழ்ப்புறம் முழுத் திரை"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"நிலை <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. திருத்த, இருமுறை தட்டவும்."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. சேர்க்க, இருமுறை தட்டவும்."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ நகர்த்தவும்"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"நிலைப்பாடு <xliff:g id="POSITION">%2$d</xliff:g>க்கு <xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ நகர்த்தும்"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> அறிவிப்பு: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"திரைப் பிரிப்பில் ஆப்ஸ் வேலைசெய்யாமல் போகக்கூடும்."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"திரையைப் பிரிப்பதைப் ஆப்ஸ் ஆதரிக்கவில்லை."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"இரண்டாம்நிலைத் திரைகளில் பயன்பாட்டைத் தொடங்க முடியாது."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"அமைப்புகளைத் திற."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"விரைவு அமைப்புகளைத் திற."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"விரைவு அமைப்புகளை மூடு."</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 3e94b72..a49bb9b 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string>
     <string name="other" msgid="429768510980739978">"ఇతరం"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"విభజన స్క్రీన్ విభాగిని"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"ఎడమవైపు పూర్తి స్క్రీన్"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ఎడమవైపు 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ఎడమవైపు 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ఎడమవైపు 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"కుడివైపు పూర్తి స్క్రీన్"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"ఎగువ పూర్తి స్క్రీన్"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ఎగువ 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ఎగువ 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ఎగువ 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"దిగువ పూర్తి స్క్రీన్"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. సవరించడానికి రెండుసార్లు నొక్కండి."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. జోడించడానికి రెండుసార్లు నొక్కండి."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ని తరలిస్తుంది"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"స్థానం <xliff:g id="POSITION">%2$d</xliff:g>కి <xliff:g id="TILE_NAME">%1$s</xliff:g>ని తరలించండి"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్‌ల ఎడిటర్."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> నోటిఫికేషన్: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ప్రత్యామ్నాయ డిస్‌ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"సెట్టింగ్‌లను తెరవండి."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"శీఘ్ర సెట్టింగ్‌లను తెరవండి."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"శీఘ్ర సెట్టింగ్‌లను మూసివేయండి."</string>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index b2d057b..6630401 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -33,14 +33,12 @@
         <item>com.android.systemui.power.PowerUI</item>
         <item>com.android.systemui.media.RingtonePlayer</item>
         <item>com.android.systemui.keyboard.KeyboardUI</item>
-        <item>com.android.systemui.pip.PipUI</item>
         <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
         <item>@string/config_systemUIVendorServiceComponent</item>
         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
         <item>com.android.systemui.SizeCompatModeActivityController</item>
         <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
         <item>com.android.systemui.toast.ToastUI</item>
-        <item>com.android.systemui.onehanded.OneHandedUI</item>
         <item>com.android.systemui.wmshell.WMShell</item>
     </string-array>
 
diff --git a/packages/SystemUI/res/values-television/integers.xml b/packages/SystemUI/res/values-television/integers.xml
index 91e83cc..587497e 100644
--- a/packages/SystemUI/res/values-television/integers.xml
+++ b/packages/SystemUI/res/values-television/integers.xml
@@ -17,6 +17,7 @@
 <resources>
     <!-- The position of the volume dialog on the screen.
          See com.android.systemui.volume.VolumeDialogImpl.
-         Value 81 corresponds to BOTTOM|CENTER_HORIZONTAL. -->
-    <integer name="volume_dialog_gravity">81</integer>
-</resources>
\ No newline at end of file
+         Value 81 corresponds to BOTTOM|CENTER_HORIZONTAL.
+         Value 21 corresponds to RIGHT|CENTER_VERTICAL. -->
+    <integer name="volume_dialog_gravity">21</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 088b624..8fd8442 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"แสดงไอคอนการแจ้งเตือนลำดับความสำคัญต่ำ"</string>
     <string name="other" msgid="429768510980739978">"อื่นๆ"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"เส้นแบ่งหน้าจอ"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"เต็มหน้าจอทางซ้าย"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"ซ้าย 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"ซ้าย 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"ซ้าย 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"เต็มหน้าจอทางขวา"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"เต็มหน้าจอด้านบน"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"ด้านบน 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"ด้านบน 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"ด้านบน 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"เต็มหน้าจอด้านล่าง"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g> <xliff:g id="TILE_NAME">%2$s</xliff:g> แตะ 2 ครั้งเพื่อแก้ไข"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> แตะ 2 ครั้งเพื่อเพิ่ม"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g> ไปยังตำแหน่ง <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> การแจ้งเตือน: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"แอปอาจใช้ไม่ได้กับโหมดแยกหน้าจอ"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"แอปไม่สนับสนุนการแยกหน้าจอ"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"แอปไม่รองรับการเรียกใช้ในจอแสดงผลรอง"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"เปิดการตั้งค่า"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"เปิดการตั้งค่าด่วน"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"ปิดการตั้งค่าด่วน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index e787b17..5951ba2 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Ipakita ang mga icon ng notification na may mababang priority"</string>
     <string name="other" msgid="429768510980739978">"Iba pa"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Divider ng split-screen"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"I-full screen ang nasa kaliwa"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Gawing 70% ang nasa kaliwa"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Gawing 50% ang nasa kaliwa"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Gawing 30% ang nasa kaliwa"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"I-full screen ang nasa kanan"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"I-full screen ang nasa itaas"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Gawing 70% ang nasa itaas"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Gawing 50% ang nasa itaas"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Gawing 30% ang nasa itaas"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"I-full screen ang nasa ibaba"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. I-double tap upang i-edit."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. I-double tap upang idagdag."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Ilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Ilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g> sa posisyong <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification sa <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Maaaring hindi gumana ang app sa split-screen."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Hindi sinusuportahan ng app ang split-screen."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Maaaring hindi gumana ang app sa pangalawang display."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Hindi sinusuportahan ng app ang paglulunsad sa mga pangalawang display."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buksan ang mga setting."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Buksan ang mga mabilisang setting."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Isara ang mga mabilisang setting."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index e339445..2d2ccad 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Düşük öncelikli bildirim simgelerini göster"</string>
     <string name="other" msgid="429768510980739978">"Diğer"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Bölünmüş ekran ayırıcı"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Solda tam ekran"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Solda %70"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Solda %50"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Solda %30"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Sağda tam ekran"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Üstte tam ekran"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Üstte %70"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Üstte %50"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Üstte %30"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Altta tam ekran"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. konum, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Düzenlemek için iki kez dokunun."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Eklemek için iki kez dokunun."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusunu taşı"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> öğesini <xliff:g id="POSITION">%2$d</xliff:g> konumuna taşı"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirimi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Uygulama bölünmüş ekranda çalışmayabilir."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Uygulama bölünmüş ekranı desteklemiyor."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Uygulama ikincil ekranda çalışmayabilir."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Uygulama ikincil ekranlarda başlatılmayı desteklemiyor."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları aç."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Hızlı ayarları aç."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Hızlı ayarları kapat."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index f5419f1..1a8ce7b 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -889,17 +889,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
     <string name="other" msgid="429768510980739978">"Інше"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Розділювач екрана"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Ліве вікно на весь екран"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Ліве вікно на 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Ліве вікно на 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Ліве вікно на 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Праве вікно на весь екран"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Верхнє вікно на весь екран"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Верхнє вікно на 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Верхнє вікно на 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Верхнє вікно на 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Нижнє вікно на весь екран"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Двічі торкніться, щоб змінити."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двічі торкніться, щоб додати."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Перемістити <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -908,10 +897,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Перемістити <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицію <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Сповіщення <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Додаток може не працювати в режимі розділеного екрана."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Додаток не підтримує розділення екрана."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Додаток може не працювати на додатковому екрані."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Додаток не підтримує запуск на додаткових екранах."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Відкрити налаштування."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Відкрити швидкі налаштування."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Закрити швидкі налаштування."</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 2abf350..4f091d6 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"کم ترجیحی اطلاع کے آئیکنز دکھائیں"</string>
     <string name="other" msgid="429768510980739978">"دیگر"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"سپلٹ اسکرین تقسیم کار"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"بائیں فل اسکرین"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"بائیں %70"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"بائیں %50"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"بائیں %30"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"دائیں فل اسکرین"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"بالائی فل اسکرین"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"اوپر %70"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"اوپر %50"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"اوپر %30"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"نچلی فل اسکرین"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>۔ ترمیم کرنے کیلئے دو بار تھپتھپائیں۔"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>۔ شامل کرنے کیلئے دو بار تھپتھپائیں۔"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> کو منتقل کریں"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> کو پوزیشن <xliff:g id="POSITION">%2$d</xliff:g> میں منتقل کریں"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> اطلاع: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"ممکن ہے کہ ایپ سپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"ایپ ثانوی ڈسپلیز پر شروعات کا تعاون نہیں کرتی۔"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ترتیبات کھولیں۔"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"فوری ترتیبات کھولیں۔"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"فوری ترتیبات بند کریں۔"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 61b10a4..26a691a 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Muhim boʻlmagan bildirishnoma ikonkalarini koʻrsatish"</string>
     <string name="other" msgid="429768510980739978">"Boshqa"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Ekranni ikkiga bo‘lish chizig‘i"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Chapda to‘liq ekran"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Chapda 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Chapda 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Chapda 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"O‘ngda to‘liq ekran"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Tepada to‘liq ekran"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Tepada 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Tepada 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Tepada 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Pastda to‘liq ekran"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>-joy, “<xliff:g id="TILE_NAME">%2$s</xliff:g>” tugmasi. Tahrirlash uchun ustiga ikki marta bosing."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasi. Qo‘shish uchun ustiga ikki marta bosing."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasini ko‘chirish"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g>-joyga buni ko‘chirish: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirishnomasi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Ilova ekranni ikkiga bo‘lish rejimini qo‘llab-quvvatlamaydi."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Bu ilova qo‘shimcha ekranlarda ishga tushmaydi."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Sozlamalarni ochish."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Tezkor sozlamalarni ochish."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Tezkor sozlamalarni yopish."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 6107270..bea99a9 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Hiển thị biểu tượng thông báo có mức ưu tiên thấp"</string>
     <string name="other" msgid="429768510980739978">"Khác"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Bộ chia chia đôi màn hình"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Toàn màn hình bên trái"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Trái 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Trái 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Trái 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Toàn màn hình bên phải"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Toàn màn hình phía trên"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Trên 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Trên 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Trên 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Toàn màn hình phía dưới"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Nhấn đúp để chỉnh sửa."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Nhấn đúp để thêm."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Di chuyển <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Di chuyển <xliff:g id="TILE_NAME">%1$s</xliff:g> đến vị trí <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Thông báo của <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Ứng dụng không hỗ trợ khởi chạy trên màn hình phụ."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Mở phần cài đặt."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Mở cài đặt nhanh."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Đóng cài đặt nhanh."</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index ffd1995..8e73f36 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"显示低优先级的通知图标"</string>
     <string name="other" msgid="429768510980739978">"其他"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"分屏分隔线"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"左侧全屏"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"左侧 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"左侧 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"左侧 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"右侧全屏"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"顶部全屏"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"顶部 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"顶部 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"顶部 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"底部全屏"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。点按两次即可修改。"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。点按两次即可添加。"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移动<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"将“<xliff:g id="TILE_NAME">%1$s</xliff:g>”移动到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"应用可能无法在分屏模式下正常运行。"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"应用不支持分屏。"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"应用可能无法在辅显示屏上正常运行。"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"应用不支持在辅显示屏上启动。"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"打开设置。"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"开启快捷设置。"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"关闭快捷设置。"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 1575427..08bf63b 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
     <string name="other" msgid="429768510980739978">"其他"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"分割畫面分隔線"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"左邊全螢幕"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"左邊 70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"左邊 50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"左邊 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"右邊全螢幕"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"頂部全螢幕"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"頂部 70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"頂部 50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"頂部 30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"底部全螢幕"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。輕按兩下即可編輯。"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕按兩下即可新增。"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"將「<xliff:g id="TILE_NAME">%1$s</xliff:g>」移去位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"應用程式可能無法在分割畫面中運作。"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"應用程式不支援分割畫面。"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"應用程式可能無法在次要顯示屏上運作。"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"應用程式無法在次要顯示屏上啟動。"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"開啟快速設定。"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"關閉快速設定。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index bbd8355..3e7d2e4 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
     <string name="other" msgid="429768510980739978">"其他"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"分割畫面分隔線"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"以全螢幕顯示左側畫面"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"以 70% 的螢幕空間顯示左側畫面"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"以 50% 的螢幕空間顯示左側畫面"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"以 30% 的螢幕空間顯示左側畫面"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"以全螢幕顯示右側畫面"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"以全螢幕顯示頂端畫面"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"以 70% 的螢幕空間顯示頂端畫面"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"以 50% 的螢幕空間顯示頂端畫面"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"以 30% 的螢幕空間顯示頂端畫面"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"以全螢幕顯示底部畫面"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。輕觸兩下即可編輯。"</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕觸兩下即可新增。"</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"將 <xliff:g id="TILE_NAME">%1$s</xliff:g> 移到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"應用程式可能無法在分割畫面中運作。"</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"這個應用程式不支援分割畫面。"</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"應用程式可能無法在次要顯示器上運作。"</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"應用程式無法在次要顯示器上啟動。"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"開啟快速設定。"</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"關閉快速設定。"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 63b4c61..f93ff1d 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -879,17 +879,6 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Bonisa izithonjana zesaziso zokubaluleka okuncane"</string>
     <string name="other" msgid="429768510980739978">"Okunye"</string>
-    <string name="accessibility_divider" msgid="2830785970889237307">"Isihlukanisi sokuhlukanisa isikrini"</string>
-    <string name="accessibility_action_divider_left_full" msgid="7598733539422375847">"Isikrini esigcwele esingakwesokunxele"</string>
-    <string name="accessibility_action_divider_left_70" msgid="4919312892541727761">"Kwesokunxele ngo-70%"</string>
-    <string name="accessibility_action_divider_left_50" msgid="3664701169564893826">"Kwesokunxele ngo-50%"</string>
-    <string name="accessibility_action_divider_left_30" msgid="4358145268046362088">"Kwesokunxele ngo-30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="8576057422864896305">"Isikrini esigcwele esingakwesokudla"</string>
-    <string name="accessibility_action_divider_top_full" msgid="4243901660795169777">"Isikrini esigcwele esiphezulu"</string>
-    <string name="accessibility_action_divider_top_70" msgid="6941226213260515072">"Okuphezulu okungu-70%"</string>
-    <string name="accessibility_action_divider_top_50" msgid="6275211443706497621">"Okuphezulu okungu-50%"</string>
-    <string name="accessibility_action_divider_top_30" msgid="5780597635887574916">"Okuphezulu okungu-30%"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="7352434720610115395">"Ngaphansi kwesikrini esigcwele"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Isimo esingu-<xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Thepha kabili ukuze uhlele."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Thepha kabili ukuze ungeze."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Hambisa i-<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
@@ -898,10 +887,6 @@
     <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Hambisa i-<xliff:g id="TILE_NAME">%1$s</xliff:g> ukuze ubeke i-<xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> isaziso: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="4689301323912928801">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7284915968096153808">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
-    <string name="forced_resizable_secondary_display" msgid="522558907654394940">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
-    <string name="activity_launch_on_secondary_display_failed_text" msgid="8446727617187998208">"Uhlelo lokusebenza alusekeli ukuqalisa kuzibonisi zesibili."</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Vula izilungiselelo."</string>
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Vula izilungiselelo ezisheshayo."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Vala izilungiselelo ezisheshayo."</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 130bb4f..ab09a96 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -161,9 +161,6 @@
     <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) -->
     <integer name="ambient_notification_extension_time">10000</integer>
 
-    <!-- Animation duration when using long press on recents to dock -->
-    <integer name="long_press_dock_anim_duration">250</integer>
-
     <!-- Whether to enable KeyguardService or not -->
     <bool name="config_enableKeyguardService">true</bool>
 
@@ -306,7 +303,6 @@
         <item>com.android.systemui.power.PowerUI</item>
         <item>com.android.systemui.media.RingtonePlayer</item>
         <item>com.android.systemui.keyboard.KeyboardUI</item>
-        <item>com.android.systemui.pip.PipUI</item>
         <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
         <item>@string/config_systemUIVendorServiceComponent</item>
         <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
@@ -321,7 +317,6 @@
         <item>com.android.systemui.accessibility.WindowMagnification</item>
         <item>com.android.systemui.accessibility.SystemActions</item>
         <item>com.android.systemui.toast.ToastUI</item>
-        <item>com.android.systemui.onehanded.OneHandedUI</item>
         <item>com.android.systemui.wmshell.WMShell</item>
     </string-array>
 
@@ -565,9 +560,6 @@
     <!-- If the config font scale is >= this value, potentially adjust the number of columns-->
     <item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item>
 
-    <!-- Allow one handed to enable round corner -->
-    <bool name="config_one_handed_enable_round_corner">true</bool>
-
     <!-- Show a separate icon for low and high volume on the volume dialog -->
     <bool name="config_showLowMediaVolumeIcon">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d12f010..875fe14 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -954,12 +954,6 @@
     <dimen name="fab_elevation">12dp</dimen>
     <dimen name="fab_press_translation_z">9dp</dimen>
 
-    <!-- How high we lift the divider when touching -->
-    <dimen name="docked_stack_divider_lift_elevation">4dp</dimen>
-
-    <dimen name="docked_divider_handle_width">16dp</dimen>
-    <dimen name="docked_divider_handle_height">2dp</dimen>
-
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
 
@@ -1261,6 +1255,8 @@
     <dimen name="qs_footer_horizontal_margin">22dp</dimen>
     <dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
     <dimen name="qs_media_enabled_seekbar_height">3dp</dimen>
+    <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen>
+    <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen>
 
     <!-- Window magnification -->
     <dimen name="magnification_border_drag_size">35dp</dimen>
@@ -1321,15 +1317,19 @@
     <dimen name="controls_management_side_padding">16dp</dimen>
     <dimen name="controls_management_titles_margin">16dp</dimen>
     <dimen name="controls_management_footer_side_margin">8dp</dimen>
+    <dimen name="controls_management_footer_top_margin">@dimen/controls_management_footer_side_margin</dimen>
     <dimen name="controls_management_list_margin">16dp</dimen>
+    <dimen name="controls_management_indicator_top_margin">@dimen/controls_management_list_margin</dimen>
     <dimen name="controls_management_apps_list_margin">64dp</dimen>
     <dimen name="controls_management_editing_list_margin">48dp</dimen>
     <dimen name="controls_management_editing_divider_margin">24dp</dimen>
     <dimen name="controls_management_apps_extra_side_margin">8dp</dimen>
     <dimen name="controls_management_zone_top_margin">32dp</dimen>
+    <dimen name="controls_management_favorites_top_margin">@dimen/controls_management_zone_top_margin</dimen>
     <dimen name="controls_management_status_side_margin">16dp</dimen>
     <dimen name="controls_management_page_indicator_height">24dp</dimen>
     <dimen name="controls_management_checkbox_size">25dp</dimen>
+    <dimen name="controls_management_footer_height">72dp</dimen>
     <dimen name="controls_title_size">24sp</dimen>
     <dimen name="controls_subtitle_size">16sp</dimen>
 
@@ -1366,8 +1366,4 @@
     <dimen name="config_rounded_mask_size">@*android:dimen/rounded_corner_radius</dimen>
     <dimen name="config_rounded_mask_size_top">@*android:dimen/rounded_corner_radius_top</dimen>
     <dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
-
-    <!-- One-Handed Mode -->
-    <!-- Threshold for dragging distance to enable one-handed mode -->
-    <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index a56f6f5..2f018b9 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -103,13 +103,6 @@
     <item type="id" name="contains_transformed_view" />
     <item type="id" name="is_clicked_heads_up_tag" />
 
-    <!-- Accessibility actions for the docked stack divider -->
-    <item type="id" name="action_move_tl_full" />
-    <item type="id" name="action_move_tl_70" />
-    <item type="id" name="action_move_tl_50" />
-    <item type="id" name="action_move_tl_30" />
-    <item type="id" name="action_move_rb_full" />
-
     <item type="id" name="bottom_roundess_animator_tag"/>
     <item type="id" name="bottom_roundess_animator_start_tag"/>
     <item type="id" name="bottom_roundess_animator_end_tag"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bfa7532..cca70f9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2260,31 +2260,6 @@
     <!-- SysUI Tuner: Other section -->
     <string name="other">Other</string>
 
-    <!-- Accessibility label for the divider that separates the windows in split-screen mode [CHAR LIMIT=NONE] -->
-    <string name="accessibility_divider">Split-screen divider</string>
-
-    <!-- Accessibility action for moving docked stack divider to make the left screen full screen [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_left_full">Left full screen</string>
-    <!-- Accessibility action for moving docked stack divider to make the left screen 70% [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_left_70">Left 70%</string>
-    <!-- Accessibility action for moving docked stack divider to make the left screen 50% [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_left_50">Left 50%</string>
-    <!-- Accessibility action for moving docked stack divider to make the left screen 30% [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_left_30">Left 30%</string>
-    <!-- Accessibility action for moving docked stack divider to make the right screen full screen [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_right_full">Right full screen</string>
-
-    <!-- Accessibility action for moving docked stack divider to make the top screen full screen [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_top_full">Top full screen</string>
-    <!-- Accessibility action for moving docked stack divider to make the top screen 70% [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_top_70">Top 70%</string>
-    <!-- Accessibility action for moving docked stack divider to make the top screen 50% [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_top_50">Top 50%</string>
-    <!-- Accessibility action for moving docked stack divider to make the top screen 30% [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_top_30">Top 30%</string>
-    <!-- Accessibility action for moving docked stack divider to make the bottom screen full screen [CHAR LIMIT=NONE] -->
-    <string name="accessibility_action_divider_bottom_full">Bottom full screen</string>
-
     <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
     <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string>
 
@@ -2312,16 +2287,6 @@
     <!-- Label for button that reports a touch that was wrongly rejected by the lockscreen. For debugging only. [CHAR LIMIT=NONE] -->
     <string name="report_rejected_touch" translatable="false">Report rejected touch</string>
 
-    <!-- Multi-Window strings -->
-    <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed in split-screen and that things might crash/not work properly [CHAR LIMIT=NONE] -->
-    <string name="dock_forced_resizable">App may not work with split-screen.</string>
-    <!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead. -->
-    <string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
-    <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] -->
-    <string name="forced_resizable_secondary_display">App may not work on a secondary display.</string>
-    <!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. -->
-    <string name="activity_launch_on_secondary_display_failed_text">App does not support launch on secondary displays.</string>
-
     <!-- accessibility label for button to open settings [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_settings">Open settings.</string>
 
@@ -2834,9 +2799,4 @@
     <string name="udfps_hbm_enable_command" translatable="false"></string>
     <!-- Device-specific payload for disabling the high-brightness mode -->
     <string name="udfps_hbm_disable_command" translatable="false"></string>
-
-    <!-- One-Handed Tutorial title [CHAR LIMIT=60] -->
-    <string name="one_handed_tutorial_title">Using one-handed mode</string>
-    <!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
-    <string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ee07e61..58563f4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -24,21 +24,6 @@
         <item name="android:layout_marginBottom">0dp</item>
     </style>
 
-    <!-- Theme used for the activity that shows when the system forced an app to be resizable -->
-    <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
-        <item name="android:windowBackground">@drawable/forced_resizable_background</item>
-        <item name="android:statusBarColor">@*android:color/transparent</item>
-        <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
-    </style>
-
-    <style name="Animation.ForcedResizable" parent="@android:style/Animation">
-        <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
-
-        <!-- If the target stack doesn't have focus, we do a task to front animation. -->
-        <item name="android:taskToFrontEnterAnimation">@anim/forced_resizable_enter</item>
-        <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
-    </style>
-
     <style name="PipPhoneOverlayControlTheme" parent="@android:style/Theme.Material">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowNoTitle">true</item>
@@ -483,23 +468,6 @@
         <item name="android:background">@drawable/btn_borderless_rect</item>
     </style>
 
-    <style name="DockedDividerBackground">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">10dp</item>
-        <item name="android:layout_gravity">center_vertical</item>
-    </style>
-
-    <style name="DockedDividerMinimizedShadow">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">8dp</item>
-    </style>
-
-    <style name="DockedDividerHandle">
-        <item name="android:layout_gravity">center_horizontal</item>
-        <item name="android:layout_width">96dp</item>
-        <item name="android:layout_height">48dp</item>
-    </style>
-
     <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:windowActionBar">false</item>
         <item name="preferenceTheme">@style/TunerPreferenceTheme</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
index 2b1fce8..ffde841 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java
@@ -41,16 +41,6 @@
     }
 
     /**
-     * Creates a dispatcher from the extras received as part on onInitialize
-     */
-    public static InputEventReceiver fromBundle(Bundle params, String key,
-            Looper looper, Choreographer choreographer, InputEventListener listener) {
-
-        InputChannel channel = params.getParcelable(key);
-        return new InputEventReceiver(channel, looper, choreographer, listener);
-    }
-
-    /**
      * Version of addBatch method which preserves time accuracy in nanoseconds instead of
      * converting the time to milliseconds.
      * @param src old MotionEvent where the target should be appended
@@ -69,11 +59,9 @@
     public static class InputEventReceiver {
 
         private final BatchedInputEventReceiver mReceiver;
-        private final InputChannel mInputChannel;
 
         public InputEventReceiver(InputChannel inputChannel, Looper looper,
                 Choreographer choreographer, final InputEventListener listener) {
-            mInputChannel = inputChannel;
             mReceiver = new BatchedInputEventReceiver(inputChannel, looper, choreographer) {
 
                 @Override
@@ -85,40 +73,17 @@
         }
 
         /**
+         * @see BatchedInputEventReceiver#setBatchingEnabled()
+         */
+        public void setBatchingEnabled(boolean batchingEnabled) {
+            mReceiver.setBatchingEnabled(batchingEnabled);
+        }
+
+        /**
          * @see BatchedInputEventReceiver#dispose()
          */
         public void dispose() {
             mReceiver.dispose();
-            mInputChannel.dispose();
-        }
-    }
-
-    /**
-     * @see InputEventSender
-     */
-    public static class InputEventDispatcher {
-
-        private final InputChannel mInputChannel;
-        private final InputEventSender mSender;
-
-        public InputEventDispatcher(InputChannel inputChannel, Looper looper) {
-            mInputChannel = inputChannel;
-            mSender = new InputEventSender(inputChannel, looper) { };
-        }
-
-        /**
-         * @see InputEventSender#sendInputEvent(int, InputEvent)
-         */
-        public void dispatch(InputEvent ev) {
-            mSender.sendInputEvent(ev.getSequenceNumber(), ev);
-        }
-
-        /**
-         * @see InputEventSender#dispose()
-         */
-        public void dispose() {
-            mSender.dispose();
-            mInputChannel.dispose();
         }
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index d64bf77..067ac9e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -153,12 +153,9 @@
         }
     }
 
+    @Deprecated
     public void setPipVisibility(final boolean visible) {
-        try {
-            WindowManagerGlobal.getWindowManagerService().setPipVisibility(visible);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to reach window manager", e);
-        }
+        // To be removed
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b048333..0517227 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -796,16 +796,6 @@
                 securityMode != SecurityMode.None && newView.needsInput());
     }
 
-    private KeyguardSecurityViewFlipper getFlipper() {
-        for (int i = 0; i < getChildCount(); i++) {
-            View child = getChildAt(i);
-            if (child instanceof KeyguardSecurityViewFlipper) {
-                return (KeyguardSecurityViewFlipper) child;
-            }
-        }
-        return null;
-    }
-
     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
         public void userActivity() {
             if (mSecurityCallback != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 35a2392..2470b95 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -86,7 +86,10 @@
                         mLiveData.observeForever(mObserver);
                     }
                     mConfigurationController.addCallback(mConfigurationListener);
-                    mDumpManager.registerDumpable(TAG, KeyguardSliceViewController.this);
+                    mDumpManager.registerDumpable(
+                            TAG + "@" + Integer.toHexString(
+                                    KeyguardSliceViewController.this.hashCode()),
+                            KeyguardSliceViewController.this);
                 }
 
                 @Override
@@ -230,5 +233,7 @@
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("  mSlice: " + mSlice);
         pw.println("  mClickActions: " + mClickActions);
+
+        mKeyguardStatusView.dump(fd, pw, args);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c354241..1027329 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2519,13 +2519,11 @@
         if (isUserUnlocked(getCurrentUser())) {
             return false;
         }
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME);
-        ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(homeIntent,
-                0 /* flags */);
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
+        ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(homeIntent,
+                0 /* flags */, getCurrentUser());
 
-        // TODO(b/160971249): Replace in the future by resolving activity as user.
-        if (resolveInfo == null && mIsAutomotive) {
+        if (resolveInfo == null) {
             Log.w(TAG, "resolveNeedsSlowUnlockTransition: returning false since activity "
                     + "could not be resolved.");
             return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 6a90d00..9766ee1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -21,9 +21,7 @@
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 
-import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
@@ -180,22 +178,18 @@
 
     /**
      * Registers the StatusBar to which this Keyguard View is mounted.
-     *
      * @param statusBar
      * @param container
      * @param notificationPanelViewController
      * @param biometricUnlockController
-     * @param dismissCallbackRegistry
      * @param lockIconContainer
      * @param notificationContainer
      * @param bypassController
-     * @param falsingManager
      */
     void registerStatusBar(StatusBar statusBar,
             ViewGroup container,
             NotificationPanelViewController notificationPanelViewController,
             BiometricUnlockController biometricUnlockController,
-            DismissCallbackRegistry dismissCallbackRegistry,
             ViewGroup lockIconContainer, View notificationContainer,
-            KeyguardBypassController bypassController, FalsingManager falsingManager);
+            KeyguardBypassController bypassController);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
rename to packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
index 114c30e..e65f19d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.keyguard.dagger;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -26,5 +26,5 @@
 @Qualifier
 @Documented
 @Retention(RUNTIME)
-public @interface PipMenuActivityClass {
+public @interface ContainerView {
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
new file mode 100644
index 0000000..84deaca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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.keyguard.dagger;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Dagger Subcomponent for the {@link KeyguardBouncer}.
+ */
+@Subcomponent
+@KeyguardBouncerScope
+public interface KeyguardBouncerComponent {
+    /** Simple factory for {@link KeyguardBouncerComponent}. */
+    @Subcomponent.Factory
+    interface Factory {
+        KeyguardBouncerComponent build(
+                @BindsInstance @ContainerView ViewGroup container,
+                @BindsInstance KeyguardBouncer.BouncerExpansionCallback bouncerExpansionCallback);
+    }
+
+    /** */
+    KeyguardBouncer createKeyguardBouncer();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
similarity index 80%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
index 114c30e..207ac28 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.keyguard.dagger;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
-import javax.inject.Qualifier;
+import javax.inject.Scope;
 
-@Qualifier
+/**
+ * Scope annotation for singleton items within the StatusBarComponent.
+ */
 @Documented
 @Retention(RUNTIME)
-public @interface PipMenuActivityClass {
-}
+@Scope
+public @interface KeyguardBouncerScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 27809b5..ed78c94 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -77,6 +77,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -88,7 +89,6 @@
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -276,7 +276,7 @@
     @Inject Lazy<StatusBarStateController> mStatusBarStateController;
     @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
     @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper;
-    @Inject Lazy<NotificationGroupManager> mNotificationGroupManager;
+    @Inject Lazy<NotificationGroupManagerLegacy> mNotificationGroupManager;
     @Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
     @Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
     @Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
@@ -468,7 +468,7 @@
         mProviders.put(NotificationLockscreenUserManager.class,
                 mNotificationLockscreenUserManager::get);
         mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get);
-        mProviders.put(NotificationGroupManager.class, mNotificationGroupManager::get);
+        mProviders.put(NotificationGroupManagerLegacy.class, mNotificationGroupManager::get);
         mProviders.put(NotificationGroupAlertTransferHelper.class,
                 mNotificationGroupAlertTransferHelper::get);
         mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 17b840cc..744a77f 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -32,10 +32,10 @@
 import android.view.ViewConfiguration;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 public class ExpandHelper implements Gefingerpoken {
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index d1149d3..f4c865e 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -21,7 +21,7 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -38,7 +38,7 @@
  *
  * NOTE: Clients of this class should take care to pass in the correct user context when querying
  * settings, otherwise you will always read/write for user 0 which is almost never what you want.
- * See {@link CurrentUserContextTracker} for a simple way to get the current context
+ * See {@link UserContextProvider} for a simple way to get the current context
  */
 public final class Prefs {
     private Prefs() {} // no instantation
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 47066a0..8aa3493c 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui;
 
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -35,8 +37,8 @@
 
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 public class SwipeHelper implements Gefingerpoken {
     static final String TAG = "com.android.systemui.SwipeHelper";
@@ -696,14 +698,15 @@
         float translation = getTranslation(mCurrView);
         return ev.getActionMasked() == MotionEvent.ACTION_UP
                 && !mFalsingManager.isUnlockingDisabled()
-                && !isFalseGesture(ev) && (swipedFastEnough() || swipedFarEnough())
+                && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough())
                 && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0);
     }
 
-    public boolean isFalseGesture(MotionEvent ev) {
+    /** Returns true if the gesture should be rejected. */
+    public boolean isFalseGesture() {
         boolean falsingDetected = mCallback.isAntiFalsingNeeded();
         if (mFalsingManager.isClassifierEnabled()) {
-            falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+            falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(NOTIFICATION_DISMISS);
         } else {
             falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index f5c3649..f159499 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -19,25 +19,16 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
-import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.dagger.DaggerGlobalRootComponent;
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.WMComponent;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
 /**
@@ -83,11 +74,16 @@
 
     public SystemUIFactory() {}
 
-    private void init(Context context) {
+    private void init(Context context) throws ExecutionException, InterruptedException {
         mRootComponent = buildGlobalRootComponent(context);
+        // Stand up WMComponent
         mWMComponent = mRootComponent.getWMComponentBuilder().build();
-        // TODO: use WMComponent to pass APIs into the SysUIComponent.
-        mSysUIComponent = mRootComponent.getSysUIComponent().build();
+
+        // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
+        // TODO: StubAPIClass is just a placeholder.
+        mSysUIComponent = mRootComponent.getSysUIComponent()
+                .setStubAPIClass(mWMComponent.createStubAPIClass())
+                .build();
 
         // Every other part of our codebase currently relies on Dependency, so we
         // really need to ensure the Dependency gets initialized early on.
@@ -101,10 +97,15 @@
                 .build();
     }
 
+
     public GlobalRootComponent getRootComponent() {
         return mRootComponent;
     }
 
+    public WMComponent getWMComponent() {
+        return mWMComponent;
+    }
+
     public SysUIComponent getSysUIComponent() {
         return mSysUIComponent;
     }
@@ -129,17 +130,4 @@
             Handler uiHandler) {
         return new ScreenshotNotificationSmartActionsProvider();
     }
-
-    public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback,
-            LockPatternUtils lockPatternUtils, ViewGroup container,
-            DismissCallbackRegistry dismissCallbackRegistry,
-            KeyguardBouncer.BouncerExpansionCallback expansionCallback,
-            KeyguardStateController keyguardStateController, FalsingManager falsingManager,
-            KeyguardBypassController bypassController) {
-        return new KeyguardBouncer(context, callback, lockPatternUtils, container,
-                dismissCallbackRegistry, falsingManager,
-                expansionCallback, keyguardStateController,
-                Dependency.get(KeyguardUpdateMonitor.class), bypassController,
-                new Handler(Looper.getMainLooper()));
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 34f721c..68e404e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -20,8 +20,13 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
+import android.graphics.PointF;
 import android.provider.Settings;
+import android.util.MathUtils;
 import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.WindowManager;
 import android.widget.ImageView;
 
@@ -31,16 +36,22 @@
 /**
  * Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
  * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
- * The button UI would automatically be dismissed after displaying for a period of time.
+ * The button icon is movable by dragging. And the button UI would automatically be dismissed after
+ * displaying for a period of time.
  */
 class MagnificationModeSwitch {
 
     private static final int DURATION_MS = 5000;
     private static final int START_DELAY_MS = 3000;
+    private final Runnable mAnimationTask;
 
     private final Context mContext;
     private final WindowManager mWindowManager;
     private final ImageView mImageView;
+    private final PointF mLastDown = new PointF();
+    private final PointF mLastDrag = new PointF();
+    private final int mTapTimeout = ViewConfiguration.getTapTimeout();
+    private final int mTouchSlop;
     private int mMagnificationMode = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
     private final WindowManager.LayoutParams mParams;
     private boolean mIsVisible = false;
@@ -56,13 +67,18 @@
                 Context.WINDOW_SERVICE);
         mParams = createLayoutParams();
         mImageView = imageView;
+        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         applyResourcesValues();
-        mImageView.setOnClickListener(
-                view -> {
-                    removeButton();
-                    toggleMagnificationMode();
-                });
         mImageView.setImageResource(getIconResId(mMagnificationMode));
+        mImageView.setOnTouchListener(this::onTouch);
+
+        mAnimationTask = () -> {
+            mImageView.animate()
+                    .alpha(0f)
+                    .setDuration(DURATION_MS)
+                    .withEndAction(() -> removeButton())
+                    .start();
+        };
     }
 
     private void applyResourcesValues() {
@@ -71,13 +87,59 @@
         mImageView.setPadding(padding, padding, padding, padding);
     }
 
+    private boolean onTouch(View v, MotionEvent event) {
+        if (!mIsVisible || mImageView == null) {
+            return false;
+        }
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mImageView.setAlpha(1.0f);
+                mImageView.animate().cancel();
+                mLastDown.set(event.getRawX(), event.getRawY());
+                mLastDrag.set(event.getRawX(), event.getRawY());
+                return true;
+            case MotionEvent.ACTION_MOVE:
+                // Move the button position.
+                moveButton(event.getRawX() - mLastDrag.x,
+                        event.getRawY() - mLastDrag.y);
+                mLastDrag.set(event.getRawX(), event.getRawY());
+                return true;
+            case MotionEvent.ACTION_UP:
+                // Single tap to toggle magnification mode and the button position will be reset
+                // after the action is performed.
+                final float distance = MathUtils.dist(mLastDown.x, mLastDown.y,
+                        event.getRawX(), event.getRawY());
+                if ((event.getEventTime() - event.getDownTime()) <= mTapTimeout
+                        && distance <= mTouchSlop) {
+                    handleSingleTap();
+                } else {
+                    showButton(mMagnificationMode);
+                }
+                return true;
+            case MotionEvent.ACTION_CANCEL:
+                showButton(mMagnificationMode);
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private void moveButton(float offsetX, float offsetY) {
+        mParams.x -= offsetX;
+        mParams.y -= offsetY;
+        mWindowManager.updateViewLayout(mImageView, mParams);
+    }
+
     void removeButton() {
         if (!mIsVisible) {
             return;
         }
         mImageView.animate().cancel();
         mWindowManager.removeView(mImageView);
+        // Reset button status.
         mIsVisible = false;
+        mParams.x = 0;
+        mParams.y = 0;
     }
 
     void showButton(int mode) {
@@ -94,13 +156,8 @@
         // Dismiss the magnification switch button after the button is displayed for a period of
         // time.
         mImageView.animate().cancel();
-        mImageView.animate()
-                .alpha(0f)
-                .setStartDelay(START_DELAY_MS)
-                .setDuration(DURATION_MS)
-                .withEndAction(
-                        () -> removeButton())
-                .start();
+        mImageView.removeCallbacks(mAnimationTask);
+        mImageView.postDelayed(mAnimationTask, START_DELAY_MS);
     }
 
     void onConfigurationChanged(int configDiff) {
@@ -120,6 +177,11 @@
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, newMode);
     }
 
+    private void handleSingleTap() {
+        removeButton();
+        toggleMagnificationMode();
+    }
+
     private static ImageView createView(Context context) {
         ImageView imageView = new ImageView(context);
         imageView.setClickable(true);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index f601c52..e10d2be 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -18,11 +18,13 @@
 
 import android.annotation.MainThread;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.SurfaceControl;
@@ -98,9 +100,11 @@
     }
 
     @MainThread
-    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
+    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+            @Nullable RemoteCallback endCallback) {
         //TODO: b/144080869 support multi-display.
-        mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY);
+        mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY,
+                endCallback != null ? () -> endCallback.sendResult(null) : null);
     }
 
     @MainThread
@@ -116,9 +120,10 @@
     }
 
     @MainThread
-    void disableWindowMagnification(int displayId) {
+    void disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
         //TODO: b/144080869 support multi-display.
-        mWindowMagnificationAnimationController.deleteWindowMagnification();
+        mWindowMagnificationAnimationController.deleteWindowMagnification(
+                endCallback != null ? () -> endCallback.sendResult(null) : null);
     }
 
     @Override
@@ -177,10 +182,10 @@
 
         @Override
         public void enableWindowMagnification(int displayId, float scale, float centerX,
-                float centerY) {
+                float centerY, RemoteCallback remoteCallback) {
             mHandler.post(
                     () -> mWindowMagnification.enableWindowMagnification(displayId, scale, centerX,
-                            centerY));
+                            centerY, remoteCallback));
         }
 
         @Override
@@ -189,8 +194,9 @@
         }
 
         @Override
-        public void disableWindowMagnification(int displayId) {
-            mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId));
+        public void disableWindowMagnification(int displayId, RemoteCallback remoteCallback) {
+            mHandler.post(() -> mWindowMagnification.disableWindowMagnification(displayId,
+                    remoteCallback));
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index ae51623..2f2e3ea 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
@@ -44,13 +45,13 @@
     @IntDef({STATE_DISABLED, STATE_ENABLED, STATE_DISABLING, STATE_ENABLING})
     @interface MagnificationState {}
 
-    //The window magnification is disabled.
+    // The window magnification is disabled.
     private static final int STATE_DISABLED = 0;
-    //The window magnification is enabled.
+    // The window magnification is enabled.
     private static final int STATE_ENABLED = 1;
-    //The window magnification is going to be disabled when the animation is end.
+    // The window magnification is going to be disabled when the animation is end.
     private  static final int STATE_DISABLING = 2;
-    //The animation is running for enabling the window magnification.
+    // The animation is running for enabling the window magnification.
     private static final int STATE_ENABLING = 3;
 
     private final WindowMagnificationController mController;
@@ -58,7 +59,11 @@
     private final AnimationSpec mStartSpec = new AnimationSpec();
     private final AnimationSpec mEndSpec = new AnimationSpec();
     private final Context mContext;
-
+    // Called when the animation is ended successfully without cancelling or mStartSpec and
+    // mEndSpec are equal.
+    private Runnable mAnimationEndCallback;
+    // The flag to ignore the animation end callback.
+    private boolean mEndAnimationCanceled = false;
     @MagnificationState
     private int mState = STATE_DISABLED;
 
@@ -83,26 +88,35 @@
      * from 1.0 and the center won't be changed during the animation. If {@link #mState} is
      * {@code STATE_DISABLING}, the animation runs in reverse.
      *
-     * @param scale   the target scale, or {@link Float#NaN} to leave unchanged.
-     * @param centerX the screen-relative X coordinate around which to center,
+     * @param scale   The target scale, or {@link Float#NaN} to leave unchanged.
+     * @param centerX The screen-relative X coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
-     * @param centerY the screen-relative Y coordinate around which to center,
+     * @param centerY The screen-relative Y coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
+     * @param animationEndCallback Called when the transition is complete or the given arguments
+     *                      are as same as current values.
      *
      * @see #onAnimationUpdate(ValueAnimator)
      */
-    void enableWindowMagnification(float scale, float centerX, float centerY) {
-        if (mState == STATE_ENABLING) {
-            mValueAnimator.cancel();
-        }
+    void enableWindowMagnification(float scale, float centerX, float centerY,
+            @Nullable Runnable animationEndCallback) {
+        mAnimationEndCallback = animationEndCallback;
         setupEnableAnimationSpecs(scale, centerX, centerY);
-
         if (mEndSpec.equals(mStartSpec)) {
+            if (mState == STATE_DISABLED) {
+                mController.enableWindowMagnification(scale, centerX, centerY);
+            } else if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
+                mValueAnimator.cancel();
+            }
+            sendCallbackIfNeeded();
             setState(STATE_ENABLED);
         } else {
             if (mState == STATE_DISABLING) {
                 mValueAnimator.reverse();
             } else {
+                if (mState == STATE_ENABLING) {
+                    mValueAnimator.cancel();
+                }
                 mValueAnimator.start();
             }
             setState(STATE_ENABLING);
@@ -115,7 +129,7 @@
         final float currentCenterY = mController.getCenterY();
 
         if (mState == STATE_DISABLED) {
-            //We don't need to offset the center during the animation.
+            // We don't need to offset the center during the animation.
             mStartSpec.set(/* scale*/ 1.0f, centerX, centerY);
             mEndSpec.set(Float.isNaN(scale) ? mContext.getResources().getInteger(
                     R.integer.magnification_default_scale) : scale, centerX, centerY);
@@ -145,9 +159,16 @@
     /**
      * Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition
      * animation. If the window magnification is enabling, it runs the animation in reverse.
+     *
+     * @param animationEndCallback Called when the transition is complete or the window
+     *                    magnification is disabled already.
      */
-    void deleteWindowMagnification() {
+    void deleteWindowMagnification(@Nullable Runnable animationEndCallback) {
+        mAnimationEndCallback = animationEndCallback;
         if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
+            if (mState == STATE_DISABLED) {
+                sendCallbackIfNeeded();
+            }
             return;
         }
         mStartSpec.set(/* scale*/ 1.0f, Float.NaN, Float.NaN);
@@ -160,9 +181,9 @@
     /**
      * Wraps {@link WindowMagnificationController#moveWindowMagnifier(float, float)}. If the
      * animation is running, it has no effect.
-     * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
+     * @param offsetX The amount in pixels to offset the window magnifier in the X direction, in
      *                current screen pixels.
-     * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
+     * @param offsetY The amount in pixels to offset the window magnifier in the Y direction, in
      *                current screen pixels.
      */
     void moveWindowMagnifier(float offsetX, float offsetY) {
@@ -185,28 +206,43 @@
 
     @Override
     public void onAnimationStart(Animator animation) {
+        mEndAnimationCanceled = false;
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation, boolean isReverse) {
+        if (mEndAnimationCanceled) {
+            return;
+        }
+        if (isReverse) {
+            mController.deleteWindowMagnification();
+            setState(STATE_DISABLED);
+        } else {
+            setState(STATE_ENABLED);
+        }
+        sendCallbackIfNeeded();
     }
 
     @Override
     public void onAnimationEnd(Animator animation) {
-        if (mState == STATE_DISABLING) {
-            mController.deleteWindowMagnification();
-            setState(STATE_DISABLED);
-        } else if (mState == STATE_ENABLING) {
-            setState(STATE_ENABLED);
-        } else {
-            Log.w(TAG, "onAnimationEnd unexpected state:" + mState);
-        }
     }
 
     @Override
     public void onAnimationCancel(Animator animation) {
+        mEndAnimationCanceled = true;
     }
 
     @Override
     public void onAnimationRepeat(Animator animation) {
     }
 
+    private void sendCallbackIfNeeded() {
+        if (mAnimationEndCallback != null) {
+            mAnimationEndCallback.run();
+            mAnimationEndCallback = null;
+        }
+    }
+
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
         final float fract = animation.getAnimatedFraction();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java
index 114c30e..d4cc3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.appops.dagger;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+import dagger.Binds;
+import dagger.Module;
 
-import javax.inject.Qualifier;
+/** Dagger Module for code in the appops package. */
+@Module
+public interface AppOpsModule {
+    /** */
+    @Binds
+    AppOpsController provideAppOpsController(AppOpsControllerImpl controllerImpl);
 
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index b9b849b..83de324 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.broadcast
 
-import android.app.ActivityManager
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
@@ -33,6 +32,7 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
 import java.io.FileDescriptor
 import java.io.PrintWriter
 import java.util.concurrent.Executor
@@ -47,8 +47,6 @@
 private const val MSG_ADD_RECEIVER = 0
 private const val MSG_REMOVE_RECEIVER = 1
 private const val MSG_REMOVE_RECEIVER_FOR_USER = 2
-private const val MSG_USER_SWITCH = 3
-private const val MSG_SET_STARTING_USER = 99
 private const val TAG = "BroadcastDispatcher"
 private const val DEBUG = true
 
@@ -68,23 +66,15 @@
     private val bgLooper: Looper,
     private val bgExecutor: Executor,
     private val dumpManager: DumpManager,
-    private val logger: BroadcastDispatcherLogger
-) : Dumpable, BroadcastReceiver() {
+    private val logger: BroadcastDispatcherLogger,
+    private val userTracker: UserTracker
+) : Dumpable {
 
     // Only modify in BG thread
     private val receiversByUser = SparseArray<UserBroadcastDispatcher>(20)
 
     fun initialize() {
         dumpManager.registerDumpable(javaClass.name, this)
-        handler.sendEmptyMessage(MSG_SET_STARTING_USER)
-        registerReceiver(this, IntentFilter(Intent.ACTION_USER_SWITCHED), null, UserHandle.ALL)
-    }
-
-    override fun onReceive(context: Context, intent: Intent) {
-        if (intent.action == Intent.ACTION_USER_SWITCHED) {
-            val user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)
-            handler.obtainMessage(MSG_USER_SWITCH, user, 0).sendToTarget()
-        }
     }
 
     /**
@@ -181,7 +171,6 @@
         pw.println("Broadcast dispatcher:")
         val ipw = IndentingPrintWriter(pw, "  ")
         ipw.increaseIndent()
-        ipw.println("Current user: ${handler.currentUser}")
         for (index in 0 until receiversByUser.size()) {
             ipw.println("User ${receiversByUser.keyAt(index)}")
             receiversByUser.valueAt(index).dump(fd, ipw, args)
@@ -190,7 +179,6 @@
     }
 
     private val handler = object : Handler(bgLooper) {
-        var currentUser = UserHandle.USER_SYSTEM
 
         override fun handleMessage(msg: Message) {
             when (msg.what) {
@@ -199,7 +187,7 @@
                     // If the receiver asked to be registered under the current user, we register
                     // under the actual current user.
                     val userId = if (data.user.identifier == UserHandle.USER_CURRENT) {
-                        currentUser
+                        userTracker.userId
                     } else {
                         data.user.identifier
                     }
@@ -221,14 +209,6 @@
                 MSG_REMOVE_RECEIVER_FOR_USER -> {
                     receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver)
                 }
-
-                MSG_USER_SWITCH -> {
-                    currentUser = msg.arg1
-                }
-                MSG_SET_STARTING_USER -> {
-                    currentUser = ActivityManager.getCurrentUser()
-                }
-
                 else -> super.handleMessage(msg)
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 27863ba..62bc425 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -694,6 +694,7 @@
         pw.print("  showInShade:   "); pw.println(showInShade());
         pw.print("  showDot:       "); pw.println(showDot());
         pw.print("  showFlyout:    "); pw.println(showFlyout());
+        pw.print("  lastActivity:  "); pw.println(getLastActivity());
         pw.print("  desiredHeight: "); pw.println(getDesiredHeightString());
         pw.print("  suppressNotif: "); pw.println(shouldSuppressNotification());
         pw.print("  autoExpand:    "); pw.println(shouldAutoExpand());
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9e9d85a..4b4e275 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -104,11 +104,11 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -164,7 +164,7 @@
     private final BubbleTaskStackListener mTaskStackListener;
     private BubbleExpandListener mExpandListener;
     @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
-    private final NotificationGroupManager mNotificationGroupManager;
+    private final NotificationGroupManagerLegacy mNotificationGroupManager;
     private final ShadeController mShadeController;
     private final FloatingContentCoordinator mFloatingContentCoordinator;
     private final BubbleDataRepository mDataRepository;
@@ -196,7 +196,7 @@
     private INotificationManager mINotificationManager;
 
     // Callback that updates BubbleOverflowActivity on data change.
-    @Nullable private Runnable mOverflowCallback = null;
+    @Nullable private BubbleData.Listener mOverflowListener = null;
 
     // Only load overflow data from disk once
     private boolean mOverflowDataLoaded = false;
@@ -355,7 +355,7 @@
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             NotificationEntryManager entryManager,
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
@@ -588,11 +588,11 @@
                     }
                 });
 
-        mNotificationGroupManager.addOnGroupChangeListener(
-                new NotificationGroupManager.OnGroupChangeListener() {
+        mNotificationGroupManager.registerGroupChangeListener(
+                new NotificationGroupManagerLegacy.OnGroupChangeListener() {
                     @Override
                     public void onGroupSuppressionChanged(
-                            NotificationGroupManager.NotificationGroup group,
+                            NotificationGroupManagerLegacy.NotificationGroup group,
                             boolean suppressed) {
                         // More notifications could be added causing summary to no longer
                         // be suppressed -- in this case need to remove the key.
@@ -650,8 +650,7 @@
                 // 3. User removes all bubbles
                 // 4. We expect all the removed bubbles AND the summary (note: the summary was
                 // never added to the suppressedSummary list in BubbleData, so we add this check)
-                NotificationEntry summary =
-                        mNotificationGroupManager.getLogicalGroupSummary(entry.getSbn());
+                NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry);
                 if (summary != null) {
                     ArrayList<NotificationEntry> summaryChildren =
                             mNotificationGroupManager.getLogicalChildren(summary.getSbn());
@@ -723,8 +722,8 @@
         mInflateSynchronously = inflateSynchronously;
     }
 
-    void setOverflowCallback(Runnable updateOverflow) {
-        mOverflowCallback = updateOverflow;
+    void setOverflowListener(BubbleData.Listener listener) {
+        mOverflowListener = listener;
     }
 
     /**
@@ -1328,9 +1327,10 @@
 
             // Lazy load overflow bubbles from disk
             loadOverflowBubblesFromDisk();
+
             // Update bubbles in overflow.
-            if (mOverflowCallback != null) {
-                mOverflowCallback.run();
+            if (mOverflowListener != null) {
+                mOverflowListener.applyUpdate(update);
             }
 
             // Collapsing? Do this first before remaining steps.
@@ -1439,21 +1439,6 @@
                 cb.invalidateNotifications("BubbleData.Listener.applyUpdate");
             }
             updateStack();
-
-            if (DEBUG_BUBBLE_CONTROLLER) {
-                Log.d(TAG, "\n[BubbleData] bubbles:");
-                Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getBubbles(),
-                        mBubbleData.getSelectedBubble()));
-
-                if (mStackView != null) {
-                    Log.d(TAG, "\n[BubbleStackView]");
-                    Log.d(TAG, BubbleDebugConfig.formatBubblesString(mStackView.getBubblesOnScreen(),
-                            mStackView.getExpandedBubble()));
-                }
-                Log.d(TAG, "\n[BubbleData] overflow:");
-                Log.d(TAG, BubbleDebugConfig.formatBubblesString(mBubbleData.getOverflowBubbles(),
-                        null) + "\n");
-            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 5c6d16d..a747db6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -75,6 +75,8 @@
         @Nullable Bubble selectedBubble;
         @Nullable Bubble addedBubble;
         @Nullable Bubble updatedBubble;
+        @Nullable Bubble addedOverflowBubble;
+        @Nullable Bubble removedOverflowBubble;
         // Pair with Bubble and @DismissReason Integer
         final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>();
 
@@ -93,10 +95,12 @@
                     || addedBubble != null
                     || updatedBubble != null
                     || !removedBubbles.isEmpty()
+                    || addedOverflowBubble != null
+                    || removedOverflowBubble != null
                     || orderChanged;
         }
 
-        void bubbleRemoved(Bubble bubbleToRemove, @DismissReason  int reason) {
+        void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
             removedBubbles.add(new Pair<>(bubbleToRemove, reason));
         }
     }
@@ -486,8 +490,9 @@
                     b.stopInflation();
                 }
                 mLogger.logOverflowRemove(b, reason);
-                mStateChange.bubbleRemoved(b, reason);
                 mOverflowBubbles.remove(b);
+                mStateChange.bubbleRemoved(b, reason);
+                mStateChange.removedOverflowBubble = b;
             }
             return;
         }
@@ -532,6 +537,7 @@
         }
         mLogger.logOverflowAdd(bubble, reason);
         mOverflowBubbles.add(0, bubble);
+        mStateChange.addedOverflowBubble = bubble;
         bubble.stopInflation();
         if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) {
             // Remove oldest bubble.
@@ -542,6 +548,7 @@
             mStateChange.bubbleRemoved(oldest, BubbleController.DISMISS_OVERFLOW_MAX_REACHED);
             mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED);
             mOverflowBubbles.remove(oldest);
+            mStateChange.removedOverflowBubble = oldest;
         }
     }
 
@@ -821,11 +828,19 @@
                 : "null");
         pw.print("expanded: ");
         pw.println(mExpanded);
-        pw.print("count:    ");
+
+        pw.print("stack bubble count:    ");
         pw.println(mBubbles.size());
         for (Bubble bubble : mBubbles) {
             bubble.dump(fd, pw, args);
         }
+
+        pw.print("overflow bubble count:    ");
+        pw.println(mOverflowBubbles.size());
+        for (Bubble bubble : mOverflowBubbles) {
+            bubble.dump(fd, pw, args);
+        }
+
         pw.print("summaryKeys: ");
         pw.println(mSuppressedGroupKeys.size());
         for (String key : mSuppressedGroupKeys.keySet()) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 80150c9..3331096 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -164,7 +164,7 @@
                     ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
                             0 /* enterResId */, 0 /* exitResId */);
                     options.setTaskAlwaysOnTop(true);
-                    options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+                    // Soptions.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
                     // Post to keep the lifecycle normal
                     post(() -> {
                         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 9926f2e..160addc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -108,14 +108,10 @@
         mEmptyStateSubtitle = findViewById(R.id.bubble_overflow_empty_subtitle);
         mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image);
 
-        updateDimensions();
-        onDataChanged(mBubbleController.getOverflowBubbles());
-        mBubbleController.setOverflowCallback(() -> {
-            onDataChanged(mBubbleController.getOverflowBubbles());
-        });
+        updateOverflow();
     }
 
-    void updateDimensions() {
+    void updateOverflow() {
         Resources res = getResources();
         final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
         mRecyclerView.setLayoutManager(
@@ -137,6 +133,22 @@
         mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
                 mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
         mRecyclerView.setAdapter(mAdapter);
+
+        mOverflowBubbles.clear();
+        mOverflowBubbles.addAll(mBubbleController.getOverflowBubbles());
+        mAdapter.notifyDataSetChanged();
+        updateEmptyStateVisibility();
+
+        mBubbleController.setOverflowListener(mDataListener);
+        updateTheme();
+    }
+
+    void updateEmptyStateVisibility() {
+        if (mOverflowBubbles.isEmpty()) {
+            mEmptyState.setVisibility(View.VISIBLE);
+        } else {
+            mEmptyState.setVisibility(View.GONE);
+        }
     }
 
     /**
@@ -168,22 +180,40 @@
         mEmptyStateSubtitle.setTextColor(textColor);
     }
 
-    void onDataChanged(List<Bubble> bubbles) {
-        mOverflowBubbles.clear();
-        mOverflowBubbles.addAll(bubbles);
-        mAdapter.notifyDataSetChanged();
+    private final BubbleData.Listener mDataListener = new BubbleData.Listener() {
 
-        if (mOverflowBubbles.isEmpty()) {
-            mEmptyState.setVisibility(View.VISIBLE);
-        } else {
-            mEmptyState.setVisibility(View.GONE);
-        }
+        @Override
+        public void applyUpdate(BubbleData.Update update) {
 
-        if (DEBUG_OVERFLOW) {
-            Log.d(TAG, "Updated overflow bubbles:\n" + BubbleDebugConfig.formatBubblesString(
-                    mOverflowBubbles, /*selected*/ null));
+            Bubble toRemove = update.removedOverflowBubble;
+            if (toRemove != null) {
+                if (DEBUG_OVERFLOW) {
+                    Log.d(TAG, "remove: " + toRemove);
+                }
+                toRemove.cleanupViews();
+                final int i = mOverflowBubbles.indexOf(toRemove);
+                mOverflowBubbles.remove(toRemove);
+                mAdapter.notifyItemRemoved(i);
+            }
+
+            Bubble toAdd = update.addedOverflowBubble;
+            if (toAdd != null) {
+                if (DEBUG_OVERFLOW) {
+                    Log.d(TAG, "add: " + toAdd);
+                }
+                mOverflowBubbles.add(0, toAdd);
+                mAdapter.notifyItemInserted(0);
+            }
+
+            updateEmptyStateVisibility();
+
+            if (DEBUG_OVERFLOW) {
+                Log.d(TAG, BubbleDebugConfig.formatBubblesString(
+                        mBubbleController.getOverflowBubbles(),
+                        null));
+            }
         }
-    }
+    };
 
     @Override
     public void onStart() {
@@ -198,8 +228,7 @@
     @Override
     public void onResume() {
         super.onResume();
-        updateDimensions();
-        updateTheme();
+        updateOverflow();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 64df2b9..c1b6882 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -276,6 +276,10 @@
     /** Description of current animation controller state. */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Stack view state:");
+
+        String bubblesOnScreen = BubbleDebugConfig.formatBubblesString(
+                getBubblesOnScreen(), getExpandedBubble());
+        pw.print("  bubbles on screen:       "); pw.println(bubblesOnScreen);
         pw.print("  gestureInProgress:       "); pw.println(mIsGestureInProgress);
         pw.print("  showingDismiss:          "); pw.println(mDismissView.isShowing());
         pw.print("  isExpansionAnimating:    "); pw.println(mIsExpansionAnimating);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index eecc41c..9efc3c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -34,8 +34,8 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -62,7 +62,7 @@
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             NotificationEntryManager entryManager,
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index 646e620..6961b45 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -70,7 +70,7 @@
     }
 
     @Override
-    public boolean isFalseTouch() {
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         return mIsFalseTouch;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index cc64fb5..decaec1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -262,7 +262,7 @@
     /**
      * @return true if the classifier determined that this is not a human interacting with the phone
      */
-    public boolean isFalseTouch() {
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         if (FalsingLog.ENABLED) {
             // We're getting some false wtfs from touches that happen after the device went
             // to sleep. Only report missing sessions that happen when the device is interactive.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 83b6df3..2c31862 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -187,8 +187,8 @@
     }
 
     @Override
-    public boolean isFalseTouch() {
-        return mInternalFalsingManager.isFalseTouch();
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+        return mInternalFalsingManager.isFalseTouch(interactionType);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index a50f9ce..9d847ca 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -189,7 +189,8 @@
     }
 
     @Override
-    public boolean isFalseTouch() {
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+        mDataProvider.setInteractionType(interactionType);
         if (!mDataProvider.isDirty()) {
             return mPreviousResult;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index ea46441..8d06748 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -116,7 +116,10 @@
      * interactionType is defined by {@link com.android.systemui.classifier.Classifier}.
      */
     final void setInteractionType(@Classifier.InteractionType int interactionType) {
-        this.mInteractionType = interactionType;
+        if (mInteractionType != interactionType) {
+            mInteractionType = interactionType;
+            mDirty = true;
+        }
     }
 
     public boolean isDirty() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 31830b9..40662536 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -263,6 +263,7 @@
         val context = itemView.context
         val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
 
+        icon.imageTintList = null
         ci.customIcon?.let {
             icon.setImageIcon(it)
         } ?: run {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 3710310..2b529f9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -104,6 +104,7 @@
     private var hidden = true
     private lateinit var dismissGlobalActions: Runnable
     private val popupThemedContext = ContextThemeWrapper(context, R.style.Control_ListPopupWindow)
+    private var retainCache = false
 
     private val collator = Collator.getInstance(context.resources.configuration.locales[0])
     private val localeComparator = compareBy<SelectionItem, CharSequence>(collator) {
@@ -149,6 +150,7 @@
         this.parent = parent
         this.dismissGlobalActions = dismissGlobalActions
         hidden = false
+        retainCache = false
 
         allStructures = controlsController.get().getFavorites()
         selectedStructure = loadPreference(allStructures)
@@ -235,6 +237,8 @@
         }
         putIntentExtras(i, si)
         startActivity(context, i)
+
+        retainCache = true
     }
 
     private fun putIntentExtras(intent: Intent, si: StructureInfo) {
@@ -497,7 +501,7 @@
 
         controlsListingController.get().removeCallback(listingCallback)
 
-        RenderInfo.clearCache()
+        if (!retainCache) RenderInfo.clearCache()
     }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
deleted file mode 100644
index e2a6d6c..0000000
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2019 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.dagger;
-
-import com.android.systemui.ActivityStarterDelegate;
-import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
-import com.android.systemui.classifier.FalsingManagerProxy;
-import com.android.systemui.controls.dagger.ControlsModule;
-import com.android.systemui.globalactions.GlobalActionsComponent;
-import com.android.systemui.globalactions.GlobalActionsImpl;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.GlobalActions;
-import com.android.systemui.plugins.VolumeDialogController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.PowerNotificationWarnings;
-import com.android.systemui.power.PowerUI;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
-import com.android.systemui.util.RingerModeTracker;
-import com.android.systemui.util.RingerModeTrackerImpl;
-import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.volume.VolumeDialogComponent;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
-
-import dagger.Binds;
-import dagger.Module;
-
-/**
- * Maps interfaces to implementations for use with Dagger.
- */
-@Module(includes = {ControlsModule.class})
-public abstract class DependencyBinder {
-
-    /**
-     */
-    @Binds
-    public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
-
-    /**
-     */
-    @Binds
-    public abstract BluetoothController provideBluetoothController(
-            BluetoothControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract GlobalActions.GlobalActionsManager provideGlobalActionsManager(
-            GlobalActionsComponent controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract LocationController provideLocationController(
-            LocationControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract RotationLockController provideRotationLockController(
-            RotationLockControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract NetworkController provideNetworkController(
-            NetworkControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract ZenModeController provideZenModeController(
-            ZenModeControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract HotspotController provideHotspotController(
-            HotspotControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract AppOpsController provideAppOpsController(
-            AppOpsControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
-            StatusBarRemoteInputCallback callbackImpl);
-
-    /**
-     */
-    @Binds
-    public abstract CastController provideCastController(CastControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract FlashlightController provideFlashlightController(
-            FlashlightControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract KeyguardStateController provideKeyguardMonitor(
-            KeyguardStateControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract UserInfoController provideUserInfoContrller(
-            UserInfoControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract ManagedProfileController provideManagedProfileController(
-            ManagedProfileControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract NextAlarmController provideNextAlarmController(
-            NextAlarmControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract SecurityController provideSecurityController(
-            SecurityControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract DarkIconDispatcher provideDarkIconDispatcher(
-            DarkIconDispatcherImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract StatusBarStateController provideStatusBarStateController(
-            StatusBarStateControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract SysuiStatusBarStateController providesSysuiStatusBarStateController(
-            StatusBarStateControllerImpl statusBarStateControllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract StatusBarIconController provideStatusBarIconController(
-            StatusBarIconControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract ExtensionController provideExtensionController(
-            ExtensionControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract VolumeDialogController provideVolumeDialogController(
-            VolumeDialogControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract SensorPrivacyController provideSensorPrivacyControllerImpl(
-            SensorPrivacyControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract QSHost provideQsHost(QSTileHost controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract VolumeComponent provideVolumeComponent(
-            VolumeDialogComponent volumeDialogComponent);
-
-    /**
-     */
-    @Binds
-    public abstract RingerModeTracker provideRingerModeTracker(
-            RingerModeTrackerImpl ringerModeTrackerImpl);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 0dd9488..38e12a6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -61,11 +61,11 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -78,6 +78,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.util.leak.LeakDetector;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -269,10 +270,11 @@
             @Background Looper backgroundLooper,
             @Background Executor backgroundExecutor,
             DumpManager dumpManager,
-            BroadcastDispatcherLogger logger
+            BroadcastDispatcherLogger logger,
+            UserTracker userTracker
     ) {
         BroadcastDispatcher bD = new BroadcastDispatcher(context, backgroundLooper,
-                backgroundExecutor, dumpManager, logger);
+                backgroundExecutor, dumpManager, logger, userTracker);
         bD.initialize();
         return bD;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index fd4a409..c5dc8cc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.dagger;
 
+import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
+
 import dagger.Module;
 
 /**
@@ -33,6 +35,8 @@
  *
  * Please use discretion when adding things to the global scope.
  */
-@Module(includes = {FrameworkServicesModule.class})
+@Module(includes = {
+        FrameworkServicesModule.class,
+        GlobalConcurrencyModule.class})
 public class GlobalModule {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 36fd337..00fdf55 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 
+import com.android.systemui.util.concurrency.ThreadFactory;
+
 import javax.inject.Singleton;
 
 import dagger.BindsInstance;
@@ -53,4 +55,9 @@
      * Builder for a SysuiComponent.
      */
     SysUIComponent.Builder getSysUIComponent();
+
+    /**
+     * Build a {@link ThreadFactory}.
+     */
+    ThreadFactory createThreadFactory();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
new file mode 100644
index 0000000..406981d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.dagger;
+
+import com.android.systemui.ActivityStarterDelegate;
+import com.android.systemui.classifier.FalsingManagerProxy;
+import com.android.systemui.globalactions.GlobalActionsComponent;
+import com.android.systemui.globalactions.GlobalActionsImpl;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.GlobalActions;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Module for binding Plugin implementations.
+ *
+ * TODO(b/166258224): Many of these should be moved closer to their implementations.
+ */
+@Module
+public interface PluginModule {
+
+    /** */
+    @Binds
+    ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
+
+    /** */
+    @Binds
+    DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherImpl controllerImpl);
+
+    /** */
+    @Binds
+    FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl);
+
+    /** */
+    @Binds
+    GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl);
+
+    /** */
+    @Binds
+    GlobalActions.GlobalActionsManager provideGlobalActionsManager(
+            GlobalActionsComponent controllerImpl);
+
+    /** */
+    @Binds
+    StatusBarStateController provideStatusBarStateController(
+            StatusBarStateControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    VolumeDialogController provideVolumeDialogController(VolumeDialogControllerImpl controllerImpl);
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index e4e3d7a..4bea067 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -22,10 +22,10 @@
 import com.android.systemui.SystemUIAppComponentFactory;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
-import com.android.systemui.pip.phone.dagger.PipModule;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.InjectionInflationController;
 
+import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
@@ -35,8 +35,6 @@
 @Subcomponent(modules = {
         DefaultComponentBinder.class,
         DependencyProvider.class,
-        DependencyBinder.class,
-        PipModule.class,
         SystemUIBinder.class,
         SystemUIModule.class,
         SystemUIDefaultModule.class})
@@ -47,6 +45,9 @@
      */
     @Subcomponent.Builder
     interface Builder {
+        @BindsInstance
+        Builder setStubAPIClass(WMComponent.StubAPIClass stubAPIClass);
+
         SysUIComponent build();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 9dfd9f8..3a5ce4d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -28,8 +28,6 @@
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
-import com.android.systemui.onehanded.OneHandedUI;
-import com.android.systemui.pip.PipUI;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsModule;
@@ -91,18 +89,6 @@
     @ClassKey(LatencyTester.class)
     public abstract SystemUI bindLatencyTester(LatencyTester sysui);
 
-    /** Inject into OneHandedUI. */
-    @Binds
-    @IntoMap
-    @ClassKey(OneHandedUI.class)
-    public abstract SystemUI bindOneHandedUI(OneHandedUI sysui);
-
-    /** Inject into PipUI. */
-    @Binds
-    @IntoMap
-    @ClassKey(PipUI.class)
-    public abstract SystemUI bindPipUI(PipUI sysui);
-
     /** Inject into PowerUI. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index a021114..2c0b04f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -46,11 +46,11 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.ShadeControllerImpl;
@@ -143,7 +143,7 @@
             Context context,
             StatusBarStateController statusBarStateController,
             KeyguardBypassController bypassController,
-            NotificationGroupManager groupManager,
+            GroupMembershipManager groupManager,
             ConfigurationController configurationController) {
         return new HeadsUpManagerPhone(context, statusBarStateController, bypassController,
                 groupManager, configurationController);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e985e3d..8f4e738 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -16,14 +16,18 @@
 
 package com.android.systemui.dagger;
 
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.BootCompleteCache;
 import com.android.systemui.BootCompleteCacheImpl;
+import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
+import com.android.systemui.controls.dagger.ControlsModule;
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.doze.dagger.DozeComponent;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.screenshot.dagger.ScreenshotModule;
 import com.android.systemui.settings.dagger.SettingsModule;
@@ -37,11 +41,15 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.util.concurrency.ConcurrencyModule;
+import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
+import com.android.systemui.tuner.dagger.TunerModule;
+import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
+import com.android.systemui.util.dagger.UtilModule;
 import com.android.systemui.util.sensors.SensorModule;
 import com.android.systemui.util.settings.SettingsUtilModule;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.util.time.SystemClockImpl;
+import com.android.systemui.volume.dagger.VolumeModule;
 
 import dagger.Binds;
 import dagger.BindsOptionalOf;
@@ -53,20 +61,29 @@
  * implementation.
  */
 @Module(includes = {
+            AppOpsModule.class,
             AssistModule.class,
-            ConcurrencyModule.class,
+            ControlsModule.class,
             DemoModeModule.class,
             LogModule.class,
             PeopleHubModule.class,
+            PowerModule.class,
+            PluginModule.class,
             ScreenshotModule.class,
             SensorModule.class,
             SettingsModule.class,
-            SettingsUtilModule.class
+            SettingsUtilModule.class,
+            StatusBarPolicyModule.class,
+            SysUIConcurrencyModule.class,
+            TunerModule.class,
+            UtilModule.class,
+            VolumeModule.class
         },
         subcomponents = {StatusBarComponent.class,
                 NotificationRowComponent.class,
                 DozeComponent.class,
                 ExpandableNotificationRowComponent.class,
+                KeyguardBouncerComponent.class,
                 NotificationShelfComponent.class,
                 FragmentService.FragmentCreator.class})
 public abstract class SystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 929b61a..ad90eff 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.dagger;
 
+import javax.inject.Inject;
+
 import dagger.Subcomponent;
 
 /**
@@ -32,4 +34,19 @@
     interface Builder {
         WMComponent build();
     }
+
+
+    /**
+     *  Example class used for passing an API to SysUI from WMShell.
+     *
+     *  TODO: Remove this once real WM classes are ready to go.
+     **/
+    @WMSingleton
+    class StubAPIClass {
+        @Inject
+        StubAPIClass() {}
+    }
+
+    /** Create a StubAPIClass. */
+    StubAPIClass createStubAPIClass();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 342818d..d9f9717 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -30,6 +30,8 @@
 import android.provider.Settings;
 import android.view.Display;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.doze.dagger.BrightnessSensor;
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.doze.dagger.WrappedService;
@@ -75,7 +77,7 @@
 
     @Inject
     public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service,
-            AsyncSensorManager sensorManager, @BrightnessSensor Sensor lightSensor,
+            AsyncSensorManager sensorManager, @Nullable @BrightnessSensor Sensor lightSensor,
             DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) {
         mContext = context;
         mDozeService = service;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 04f7c36..b899460 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -20,6 +20,8 @@
 import android.hardware.Sensor;
 import android.os.Handler;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeAuthRemover;
@@ -91,6 +93,7 @@
 
     @Provides
     @BrightnessSensor
+    @Nullable
     static Sensor providesBrightnessSensor(AsyncSensorManager sensorManager, Context context) {
         return DozeSensors.findSensorWithType(sensorManager,
                 context.getString(R.string.doze_brightness_sensor_type));
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d213ac1..9c90510 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
 import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -130,7 +131,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -252,7 +253,7 @@
     private final RingerModeTracker mRingerModeTracker;
     private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
     private Handler mMainHandler;
-    private CurrentUserContextTracker mCurrentUserContextTracker;
+    private UserContextProvider mUserContextProvider;
     @VisibleForTesting
     boolean mShowLockScreenCardsAndControls = false;
 
@@ -313,7 +314,7 @@
             UiEventLogger uiEventLogger,
             RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
             ControlsComponent controlsComponent,
-            CurrentUserContextTracker currentUserContextTracker) {
+            UserContextProvider userContextProvider) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -342,7 +343,7 @@
         mControlsControllerOptional = controlsComponent.getControlsController();
         mSysUiState = sysUiState;
         mMainHandler = handler;
-        mCurrentUserContextTracker = currentUserContextTracker;
+        mUserContextProvider = userContextProvider;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -436,7 +437,7 @@
         String[] preferredControlsPackages = mContext.getResources()
                 .getStringArray(com.android.systemui.R.array.config_controlsPreferredPackages);
 
-        SharedPreferences prefs = mCurrentUserContextTracker.getCurrentUserContext()
+        SharedPreferences prefs = mUserContextProvider.getUserContext()
                 .getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE);
         Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
                 Collections.emptySet());
@@ -548,7 +549,7 @@
         if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
             return false;
         }
-        return true;
+        return action.shouldShow();
     }
 
     /**
@@ -961,6 +962,8 @@
 
     @VisibleForTesting
     class ScreenshotAction extends SinglePressAction implements LongPressAction {
+        final String KEY_SYSTEM_NAV_2BUTTONS = "system_nav_2buttons";
+
         public ScreenshotAction() {
             super(R.drawable.ic_screenshot, R.string.global_action_screenshot);
         }
@@ -993,6 +996,19 @@
         }
 
         @Override
+        public boolean shouldShow() {
+          // Include screenshot in power menu for legacy nav because it is not accessible
+          // through Recents in that mode
+            return is2ButtonNavigationEnabled();
+        }
+
+        boolean is2ButtonNavigationEnabled() {
+            return NAV_BAR_MODE_2BUTTON == mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_navBarInteractionMode);
+        }
+
+
+        @Override
         public boolean onLongPress() {
             if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SCREENRECORD_LONG_PRESS)) {
                 mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
@@ -1615,6 +1631,10 @@
          * @return
          */
         CharSequence getMessage();
+
+        default boolean shouldShow() {
+            return true;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3340791..2705f07 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2184,8 +2184,8 @@
             BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer,
             View notificationContainer, KeyguardBypassController bypassController) {
         mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, container, panelView,
-                biometricUnlockController, mDismissCallbackRegistry, lockIconContainer,
-                notificationContainer, bypassController, mFalsingManager);
+                biometricUnlockController, lockIconContainer,
+                notificationContainer, bypassController);
         return mKeyguardViewControllerLazy.get();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index a003d83..f150381 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -42,7 +42,7 @@
     private val mediaHostStatesManager: MediaHostStatesManager,
     private val activityStarter: ActivityStarter,
     @Main executor: DelayableExecutor,
-    mediaManager: MediaDataManager,
+    private val mediaManager: MediaDataManager,
     configurationController: ConfigurationController,
     falsingManager: FalsingManager
 ) {
@@ -109,6 +109,7 @@
     private val pageIndicator: PageIndicator
     private val visualStabilityCallback: VisualStabilityManager.Callback
     private var needsReordering: Boolean = false
+    private var keysNeedRemoval = mutableSetOf<String>()
     private var isRtl: Boolean = false
         set(value) {
             if (value != field) {
@@ -161,6 +162,10 @@
                 needsReordering = false
                 reorderAllPlayers()
             }
+
+            keysNeedRemoval.forEach { removePlayer(it) }
+            keysNeedRemoval.clear()
+
             // Let's reset our scroll position
             mediaCarouselScrollHandler.scrollToStart()
         }
@@ -168,14 +173,19 @@
                 true /* persistent */)
         mediaManager.addListener(object : MediaDataManager.Listener {
             override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
-                if (!data.active && !Utils.useMediaResumption(context)) {
-                    // This view is inactive, let's remove this! This happens e.g when dismissing /
-                    // timing out a view. We still have the data around because resumption could
-                    // be on, but we should save the resources and release this.
-                    oldKey?.let { MediaPlayerData.removeMediaPlayer(it) }
-                    onMediaDataRemoved(key)
+                addOrUpdatePlayer(key, oldKey, data)
+                val canRemove = data.isPlaying?.let { !it } ?: data.isClearable
+                if (canRemove && !Utils.useMediaResumption(context)) {
+                    // This view isn't playing, let's remove this! This happens e.g when
+                    // dismissing/timing out a view. We still have the data around because
+                    // resumption could be on, but we should save the resources and release this.
+                    if (visualStabilityManager.isReorderingAllowed) {
+                        onMediaDataRemoved(key)
+                    } else {
+                        keysNeedRemoval.add(key)
+                    }
                 } else {
-                    addOrUpdatePlayer(key, oldKey, data)
+                    keysNeedRemoval.remove(key)
                 }
             }
 
@@ -237,12 +247,12 @@
             var newPlayer = mediaControlPanelFactory.get()
             newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent))
             newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions
-            MediaPlayerData.addMediaPlayer(key, data, newPlayer)
             val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT)
             newPlayer.view?.player?.setLayoutParams(lp)
             newPlayer.bind(data)
             newPlayer.setListening(currentlyExpanded)
+            MediaPlayerData.addMediaPlayer(key, data, newPlayer)
             updatePlayerToState(newPlayer, noAnimation = true)
             reorderAllPlayers()
         } else {
@@ -272,6 +282,9 @@
             removed.onDestroy()
             mediaCarouselScrollHandler.onPlayersChanged()
             updatePageIndicator()
+
+            // Inform the media manager of a potentially late dismissal
+            mediaManager.dismissMediaData(key, 0L)
         }
     }
 
@@ -479,12 +492,11 @@
 internal object MediaPlayerData {
     private data class MediaSortKey(
         val data: MediaData,
-        val updateTime: Long = 0,
-        val isPlaying: Boolean = false
+        val updateTime: Long = 0
     )
 
     private val comparator =
-        compareByDescending<MediaSortKey> { it.isPlaying }
+        compareByDescending<MediaSortKey> { it.data.isPlaying }
         .thenByDescending { it.data.isLocalSession }
         .thenByDescending { !it.data.resumption }
         .thenByDescending { it.updateTime }
@@ -494,7 +506,7 @@
 
     fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) {
         removeMediaPlayer(key)
-        val sortKey = MediaSortKey(data, System.currentTimeMillis(), player.isPlaying())
+        val sortKey = MediaSortKey(data, System.currentTimeMillis())
         mediaData.put(key, sortKey)
         mediaPlayers.put(sortKey, player)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index 77cac50..4863999 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.qs.PageIndicator
 import com.android.systemui.R
+import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.util.animation.PhysicsAnimator
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -315,7 +316,8 @@
         return false
     }
 
-    private fun isFalseTouch() = falsingProtectionNeeded && falsingManager.isFalseTouch
+    private fun isFalseTouch() = falsingProtectionNeeded &&
+            falsingManager.isFalseTouch(NOTIFICATION_DISMISS)
 
     private fun getMaxTranslation() = if (showsSettingsButton) {
             settingsButton.width
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index d6a0268..40a879a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -94,7 +94,17 @@
      * Notification key for cancelling a media player after a timeout (when not using resumption.)
      */
     val notificationKey: String? = null,
-    var hasCheckedForResume: Boolean = false
+    var hasCheckedForResume: Boolean = false,
+
+    /**
+     * If apps do not report PlaybackState, set as null to imply 'undetermined'
+     */
+    val isPlaying: Boolean? = null,
+
+    /**
+     * Set from the notification and used as fallback when PlaybackState cannot be determined
+     */
+    val isClearable: Boolean = true
 )
 
 /** State of a media action. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 0664a41..1f580a9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -136,14 +136,8 @@
 
     /**
      * Are there any media entries we should display?
-     * If resumption is enabled, this will include inactive players
-     * If resumption is disabled, we only want to show active players
      */
-    fun hasAnyMedia() = if (mediaResumeListener.isResumptionEnabled()) {
-        userEntries.isNotEmpty()
-    } else {
-        hasActiveMedia()
-    }
+    fun hasAnyMedia() = userEntries.isNotEmpty()
 
     /**
      * Add a listener for filtered [MediaData] changes
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 686531a..cb6b22c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -47,6 +47,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
@@ -350,6 +351,16 @@
     }
 
     fun dismissMediaData(key: String, delay: Long) {
+        backgroundExecutor.execute {
+            mediaEntries[key]?.let { mediaData ->
+                if (mediaData.isLocalSession) {
+                    mediaData.token?.let {
+                        val mediaController = mediaControllerFactory.create(it)
+                        mediaController.transportControls.stop()
+                    }
+                }
+            }
+        }
         foregroundExecutor.executeDelayed({ removeEntry(key) }, delay)
     }
 
@@ -500,6 +511,7 @@
 
         val isLocalSession = mediaController.playbackInfo?.playbackType ==
             MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL ?: true
+        val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
 
         foregroundExecutor.execute {
             val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
@@ -509,7 +521,8 @@
                     smallIconDrawable, artist, song, artWorkIcon, actionIcons,
                     actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
                     active, resumeAction = resumeAction, isLocalSession = isLocalSession,
-                    notificationKey = key, hasCheckedForResume = hasCheckedForResume))
+                    notificationKey = key, hasCheckedForResume = hasCheckedForResume,
+                    isPlaying = isPlaying, isClearable = sbn.isClearable()))
         }
     }
 
@@ -562,7 +575,10 @@
                 decoder, info, source -> decoder.isMutableRequired = true
             }
         } catch (e: IOException) {
-            e.printStackTrace()
+            Log.e(TAG, "Unable to load bitmap", e)
+            null
+        } catch (e: RuntimeException) {
+            Log.e(TAG, "Unable to load bitmap", e)
             null
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index c00b5e9..5b59214 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -125,8 +125,6 @@
         }, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED)
     }
 
-    fun isResumptionEnabled() = useMediaResumption
-
     private fun loadSavedComponents() {
         // Make sure list is empty (if we switched users)
         resumeComponents.clear()
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index 1ae54d6..d789501 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -28,10 +28,14 @@
  */
 class SeekBarObserver(private val holder: PlayerViewHolder) : Observer<SeekBarViewModel.Progress> {
 
-    val seekBarDefaultMaxHeight = holder.seekBar.context.resources
+    val seekBarEnabledMaxHeight = holder.seekBar.context.resources
         .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_height)
     val seekBarDisabledHeight = holder.seekBar.context.resources
         .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_height)
+    val seekBarEnabledVerticalPadding = holder.seekBar.context.resources
+            .getDimensionPixelSize(R.dimen.qs_media_enabled_seekbar_vertical_padding)
+    val seekBarDisabledVerticalPadding = holder.seekBar.context.resources
+            .getDimensionPixelSize(R.dimen.qs_media_disabled_seekbar_vertical_padding)
 
     /** Updates seek bar views when the data model changes. */
     @UiThread
@@ -39,6 +43,7 @@
         if (!data.enabled) {
             if (holder.seekBar.maxHeight != seekBarDisabledHeight) {
                 holder.seekBar.maxHeight = seekBarDisabledHeight
+                setVerticalPadding(seekBarDisabledVerticalPadding)
             }
             holder.seekBar.setEnabled(false)
             holder.seekBar.getThumb().setAlpha(0)
@@ -51,8 +56,9 @@
         holder.seekBar.getThumb().setAlpha(if (data.seekAvailable) 255 else 0)
         holder.seekBar.setEnabled(data.seekAvailable)
 
-        if (holder.seekBar.maxHeight != seekBarDefaultMaxHeight) {
-            holder.seekBar.maxHeight = seekBarDefaultMaxHeight
+        if (holder.seekBar.maxHeight != seekBarEnabledMaxHeight) {
+            holder.seekBar.maxHeight = seekBarEnabledMaxHeight
+            setVerticalPadding(seekBarEnabledVerticalPadding)
         }
 
         data.duration?.let {
@@ -67,4 +73,11 @@
                     it / DateUtils.SECOND_IN_MILLIS))
         }
     }
+
+    @UiThread
+    fun setVerticalPadding(padding: Int) {
+        val leftPadding = holder.seekBar.paddingLeft
+        val rightPadding = holder.seekBar.paddingRight
+        holder.seekBar.setPadding(leftPadding, padding, rightPadding, padding)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index aec3543..4e0df21 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -122,7 +122,6 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -137,6 +136,7 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -662,7 +662,7 @@
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 9b9dc6d..339e504 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -55,7 +55,6 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -65,6 +64,7 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 8a468f6e..13b9a55 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -87,12 +87,12 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index a1b55c4..dfc82f1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -38,11 +38,11 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
+import android.view.Choreographer;
 import android.view.ISystemGestureExclusionListener;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
-import android.view.InputEventReceiver;
 import android.view.InputMonitor;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -67,6 +67,7 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -169,7 +170,7 @@
     private boolean mGestureBlockingActivityRunning;
 
     private InputMonitor mInputMonitor;
-    private InputEventReceiver mInputEventReceiver;
+    private InputChannelCompat.InputEventReceiver mInputEventReceiver;
 
     private NavigationEdgeBackPlugin mEdgeBackPlugin;
     private int mLeftInset;
@@ -383,8 +384,9 @@
             // Register input event receiver
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "edge-swipe", mDisplayId);
-            mInputEventReceiver = new SysUiInputEventReceiver(
-                    mInputMonitor.getInputChannel(), Looper.getMainLooper());
+            mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
+                    mInputMonitor.getInputChannel(), Looper.getMainLooper(),
+                    Choreographer.getInstance(), this::onInputEvent);
 
             // Add a nav bar panel window
             setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
@@ -520,6 +522,7 @@
         if (action == MotionEvent.ACTION_DOWN) {
             // Verify if this is in within the touch region and we aren't in immersive mode, and
             // either the bouncer is showing or the notification panel is hidden
+            mInputEventReceiver.setBatchingEnabled(false);
             mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
             mLogGesture = false;
             mInRejectedExclusion = false;
@@ -571,6 +574,7 @@
                             mThresholdCrossed = true;
                             // Capture inputs
                             mInputMonitor.pilferPointers();
+                            mInputEventReceiver.setBatchingEnabled(true);
                         } else {
                             logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE);
                         }
@@ -584,7 +588,7 @@
             }
         }
 
-        Dependency.get(ProtoTracer.class).update();
+        Dependency.get(ProtoTracer.class).scheduleFrameUpdate();
     }
 
     private void updateDisabledForQuickstep() {
@@ -672,15 +676,4 @@
         }
         proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
     }
-
-    class SysUiInputEventReceiver extends InputEventReceiver {
-        SysUiInputEventReceiver(InputChannel channel, Looper looper) {
-            super(channel, looper);
-        }
-
-        public void onInputEvent(InputEvent event) {
-            EdgeBackGestureHandler.this.onInputEvent(event);
-            finishInputEvent(event, true);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java
deleted file mode 100644
index bb59449..0000000
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2020 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.onehanded;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.SystemProperties;
-import android.view.KeyEvent;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.wm.shell.common.DisplayChangeController;
-import com.android.wm.shell.common.DisplayController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import javax.inject.Inject;
-
-/**
- * Manages and manipulates the one handed states, transitions, and gesture for phones.
- */
-@SysUISingleton
-public class OneHandedController implements Dumpable {
-    private static final String TAG = "OneHandedManager";
-    private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
-            "persist.debug.one_handed_offset_percentage";
-
-    private boolean mIsOneHandedEnabled;
-    private boolean mIsSwipeToNotificationEnabled;
-    private boolean mTaskChangeToExit;
-    private float mOffSetFraction;
-
-    private final CommandQueue mCommandQueue;
-    private final DisplayController mDisplayController;
-    private final OneHandedGestureHandler mGestureHandler;
-    private final OneHandedTimeoutHandler mTimeoutHandler;
-    private final OneHandedTouchHandler mTouchHandler;
-    private final OneHandedTutorialHandler mTutorialHandler;
-    private final SysUiState mSysUiFlagContainer;
-
-    private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
-
-    /**
-     * Handler for system task stack changes, exit when user lunch new task or bring task to front
-     */
-    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
-        @Override
-        public void onTaskCreated(int taskId, ComponentName componentName) {
-            if (!mIsOneHandedEnabled || !mDisplayAreaOrganizer.isInOneHanded()) {
-                return;
-            }
-            OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT);
-            stopOneHanded();
-        }
-
-        @Override
-        public void onTaskMovedToFront(int taskId) {
-            if (!mIsOneHandedEnabled || !mDisplayAreaOrganizer.isInOneHanded()) {
-                return;
-            }
-            OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT);
-            stopOneHanded();
-        }
-    };
-
-    /**
-     * Handle rotation based on OnDisplayChangingListener callback
-     */
-    private final DisplayChangeController.OnDisplayChangingListener mRotationController =
-            (display, fromRotation, toRotation, wct) -> {
-                if (mDisplayAreaOrganizer != null) {
-                    mDisplayAreaOrganizer.onRotateDisplay(fromRotation, toRotation);
-                }
-            };
-
-    /**
-     * Constructor of OneHandedManager
-     */
-    @Inject
-    public OneHandedController(Context context,
-            CommandQueue commandQueue,
-            DisplayController displayController,
-            NavigationModeController navigationModeController,
-            SysUiState sysUiState) {
-        mCommandQueue = commandQueue;
-        mDisplayController = displayController;
-        mDisplayController.addDisplayChangingController(mRotationController);
-        mSysUiFlagContainer = sysUiState;
-        mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f;
-
-        mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
-                context.getContentResolver());
-        mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
-                context.getContentResolver());
-        mTimeoutHandler = OneHandedTimeoutHandler.get();
-        mTouchHandler = new OneHandedTouchHandler();
-        mTutorialHandler = new OneHandedTutorialHandler(context);
-        mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(context, displayController,
-                new OneHandedAnimationController(context), mTutorialHandler);
-        mGestureHandler = new OneHandedGestureHandler(
-                context, displayController, navigationModeController);
-        updateOneHandedEnabled();
-        setupGestures();
-    }
-
-    /**
-     * Constructor of OneHandedManager for testing
-     */
-    // TODO(b/161980408): Should remove extra constructor.
-    @VisibleForTesting
-    OneHandedController(Context context,
-            CommandQueue commandQueue,
-            DisplayController displayController,
-            OneHandedDisplayAreaOrganizer displayAreaOrganizer,
-            OneHandedTouchHandler touchHandler,
-            OneHandedTutorialHandler tutorialHandler,
-            OneHandedGestureHandler gestureHandler,
-            SysUiState sysUiState) {
-        mCommandQueue = commandQueue;
-        mDisplayAreaOrganizer = displayAreaOrganizer;
-        mDisplayController = displayController;
-        mDisplayController.addDisplayChangingController(mRotationController);
-        mSysUiFlagContainer = sysUiState;
-        mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f;
-
-        mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
-                context.getContentResolver());
-        mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
-                context.getContentResolver());
-        mTimeoutHandler = OneHandedTimeoutHandler.get();
-        mTouchHandler = touchHandler;
-        mTutorialHandler = tutorialHandler;
-        mGestureHandler = gestureHandler;
-        updateOneHandedEnabled();
-        setupGestures();
-    }
-
-    /**
-     * Set one handed enabled or disabled by OneHanded UI when user update settings
-     */
-    public void setOneHandedEnabled(boolean enabled) {
-        mIsOneHandedEnabled = enabled;
-        updateOneHandedEnabled();
-    }
-
-    /**
-     * Set one handed enabled or disabled by OneHanded UI when user update settings
-     */
-    public void setTaskChangeToExit(boolean enabled) {
-        if (mTaskChangeToExit == enabled) {
-            return;
-        }
-        mTaskChangeToExit = enabled;
-        updateOneHandedEnabled();
-    }
-
-    /**
-     * Sets whether to enable swipe bottom to notification gesture when user update settings.
-     */
-    public void setSwipeToNotificationEnabled(boolean enabled) {
-        mIsSwipeToNotificationEnabled = enabled;
-        updateOneHandedEnabled();
-    }
-
-    /**
-     * Enters one handed mode.
-     */
-    public void startOneHanded() {
-        if (!mDisplayAreaOrganizer.isInOneHanded()) {
-            final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
-            mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
-            mTimeoutHandler.resetTimer();
-        }
-    }
-
-    /**
-     * Exits one handed mode.
-     */
-    public void stopOneHanded() {
-        if (mDisplayAreaOrganizer.isInOneHanded()) {
-            mDisplayAreaOrganizer.scheduleOffset(0, 0);
-            mTimeoutHandler.removeTimer();
-        }
-    }
-
-    private void setupGestures() {
-        mTouchHandler.registerTouchEventListener(
-                new OneHandedTouchHandler.OneHandedTouchEventCallback() {
-                    @Override
-                    public void onStart() {
-                        if (mIsOneHandedEnabled) {
-                            startOneHanded();
-                        }
-                    }
-
-                    @Override
-                    public void onStop() {
-                        if (mIsOneHandedEnabled) {
-                            stopOneHanded();
-                        }
-                    }
-                });
-
-        mGestureHandler.setGestureEventListener(
-                new OneHandedGestureHandler.OneHandedGestureEventCallback() {
-                    @Override
-                    public void onStart() {
-                        if (mIsOneHandedEnabled) {
-                            startOneHanded();
-                        } else if (mIsSwipeToNotificationEnabled) {
-                            mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
-                        }
-                    }
-
-                    @Override
-                    public void onStop() {
-                        if (mIsOneHandedEnabled) {
-                            stopOneHanded();
-                        } else if (mIsSwipeToNotificationEnabled) {
-                            mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
-                        }
-                    }
-                });
-
-        mDisplayAreaOrganizer.registerTransitionCallback(new OneHandedTransitionCallback() {
-            @Override
-            public void onStartFinished(Rect bounds) {
-                mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
-                        true).commitUpdate(DEFAULT_DISPLAY);
-            }
-
-            @Override
-            public void onStopFinished(Rect bounds) {
-                mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
-                        false).commitUpdate(DEFAULT_DISPLAY);
-            }
-        });
-
-        mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
-        mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
-        mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
-    }
-
-    /**
-     * Query the current display real size from {@link DisplayController}
-     *
-     * @return {@link DisplayController#getDisplay(int)#getDisplaySize()}
-     */
-    private Point getDisplaySize() {
-        Point displaySize = new Point();
-        if (mDisplayController != null && mDisplayController.getDisplay(DEFAULT_DISPLAY) != null) {
-            mDisplayController.getDisplay(DEFAULT_DISPLAY).getRealSize(displaySize);
-        }
-        return displaySize;
-    }
-
-    private void updateOneHandedEnabled() {
-        if (mDisplayAreaOrganizer.isInOneHanded()) {
-            stopOneHanded();
-        }
-        // TODO Be aware to unregisterOrganizer() after animation finished
-        mDisplayAreaOrganizer.unregisterOrganizer();
-        if (mIsOneHandedEnabled) {
-            mDisplayAreaOrganizer.registerOrganizer(
-                    OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
-        }
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
-        if (mTaskChangeToExit) {
-            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-        }
-        mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
-        mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled);
-    }
-
-    @Override
-    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
-        final String innerPrefix = "  ";
-        pw.println(TAG + "states: ");
-        pw.print(innerPrefix + "mSysUiFlagContainer=");
-        pw.println(mSysUiFlagContainer.getFlags());
-        pw.print(innerPrefix + "mOffSetFraction=");
-        pw.println(mOffSetFraction);
-
-        if (mDisplayAreaOrganizer != null) {
-            mDisplayAreaOrganizer.dump(fd, pw, args);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
deleted file mode 100644
index 3348a06..0000000
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright (C) 2020 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.onehanded;
-
-import static android.os.UserHandle.USER_CURRENT;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.content.Context;
-import android.content.om.IOverlayManager;
-import android.content.om.OverlayInfo;
-import android.database.ContentObserver;
-import android.inputmethodservice.InputMethodService;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.statusbar.CommandQueue;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import javax.inject.Inject;
-
-/**
- * A service that controls UI of the one handed mode function.
- */
-@SysUISingleton
-public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dumpable {
-    private static final String TAG = "OneHandedUI";
-    private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
-            "com.android.internal.systemui.onehanded.gestural";
-    private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
-
-    private final OneHandedController mOneHandedController;
-    private final CommandQueue mCommandQueue;
-    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
-    private final IOverlayManager mOverlayManager;
-    private final OneHandedTimeoutHandler mTimeoutHandler;
-    private final ScreenLifecycle mScreenLifecycle;
-
-    private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
-                    mContext.getContentResolver());
-            OneHandedEvents.writeEvent(enabled
-                    ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
-                    : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
-            if (mOneHandedController != null) {
-                mOneHandedController.setOneHandedEnabled(enabled);
-            }
-
-            // Also checks swipe to notification settings since they all need gesture overlay.
-            setEnabledGesturalOverlay(
-                    enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
-                            mContext.getContentResolver()));
-        }
-    };
-
-    private final ContentObserver mTimeoutObserver = new ContentObserver(mMainHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
-                    mContext.getContentResolver());
-            int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId();
-            switch (newTimeout) {
-                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
-                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER;
-                    break;
-                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS:
-                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4;
-                    break;
-                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS:
-                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8;
-                    break;
-                case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS:
-                    metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12;
-                    break;
-                default:
-                    // do nothing
-                    break;
-            }
-            OneHandedEvents.writeEvent(metricsId);
-
-            if (mTimeoutHandler != null) {
-                mTimeoutHandler.setTimeout(newTimeout);
-            }
-        }
-    };
-
-    private final ContentObserver mTaskChangeExitObserver = new ContentObserver(mMainHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
-                    mContext.getContentResolver());
-            OneHandedEvents.writeEvent(enabled
-                    ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
-                    : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
-
-            if (mOneHandedController != null) {
-                mOneHandedController.setTaskChangeToExit(enabled);
-            }
-        }
-    };
-
-    private final ContentObserver mSwipeToNotificationEnabledObserver =
-            new ContentObserver(mMainHandler) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    final boolean enabled =
-                            OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
-                                    mContext.getContentResolver());
-                    if (mOneHandedController != null) {
-                        mOneHandedController.setSwipeToNotificationEnabled(enabled);
-                    }
-
-                    // Also checks one handed mode settings since they all need gesture overlay.
-                    setEnabledGesturalOverlay(
-                            enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
-                                    mContext.getContentResolver()));
-                }
-            };
-
-    @Inject
-    public OneHandedUI(Context context,
-            CommandQueue commandQueue,
-            OneHandedController oneHandedController,
-            ScreenLifecycle screenLifecycle) {
-        super(context);
-
-        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
-            Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
-            mCommandQueue = null;
-            mOneHandedController = null;
-            mOverlayManager = null;
-            mTimeoutHandler = null;
-            mScreenLifecycle = null;
-            return;
-        }
-
-        mCommandQueue = commandQueue;
-        mOneHandedController = oneHandedController;
-        mTimeoutHandler = OneHandedTimeoutHandler.get();
-        mScreenLifecycle = screenLifecycle;
-        mOverlayManager = IOverlayManager.Stub.asInterface(
-                ServiceManager.getService(Context.OVERLAY_SERVICE));
-    }
-
-    @Override
-    public void start() {
-        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
-            return;
-        }
-        mCommandQueue.addCallback(this);
-        setupKeyguardUpdateMonitor();
-        setupScreenObserver();
-        setupSettingObservers();
-        setupTimeoutListener();
-        setupGesturalOverlay();
-        updateSettings();
-    }
-
-    private void setupGesturalOverlay() {
-        if (!OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(mContext.getContentResolver())) {
-            return;
-        }
-
-        OverlayInfo info = null;
-        try {
-            // TODO(b/157958539) migrate new RRO config file after S+
-            mOverlayManager.setHighestPriority(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
-            info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
-        } catch (RemoteException e) { /* Do nothing */ }
-
-        if (info != null && !info.isEnabled()) {
-            // Enable the default gestural one handed overlay.
-            setEnabledGesturalOverlay(true);
-        }
-    }
-
-    private void setupTimeoutListener() {
-        mTimeoutHandler.registerTimeoutListener(timeoutTime -> {
-            OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT);
-            stopOneHanded();
-        });
-    }
-
-    private void setupKeyguardUpdateMonitor() {
-        final KeyguardUpdateMonitorCallback keyguardCallback =
-                new KeyguardUpdateMonitorCallback() {
-                    @Override
-                    public void onKeyguardBouncerChanged(boolean bouncer) {
-                        if (bouncer) {
-                            stopOneHanded();
-                        }
-                    }
-
-                    @Override
-                    public void onKeyguardVisibilityChanged(boolean showing) {
-                        stopOneHanded();
-                    }
-                };
-        Dependency.get(KeyguardUpdateMonitor.class).registerCallback(keyguardCallback);
-    }
-
-    @Override
-    public void onCameraLaunchGestureDetected(int source) {
-        stopOneHanded();
-    }
-
-    private void setupScreenObserver() {
-        final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
-            @Override
-            public void onScreenTurningOff() {
-                OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT);
-                stopOneHanded();
-            }
-        };
-        mScreenLifecycle.addObserver(mScreenObserver);
-    }
-
-    private void setupSettingObservers() {
-        OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
-                mContext.getContentResolver(), mEnabledObserver);
-        OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
-                mContext.getContentResolver(), mTimeoutObserver);
-        OneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT,
-                mContext.getContentResolver(), mTaskChangeExitObserver);
-        OneHandedSettingsUtil.registerSettingsKeyObserver(
-                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
-                mContext.getContentResolver(), mSwipeToNotificationEnabledObserver);
-    }
-
-    private void updateSettings() {
-        mOneHandedController.setOneHandedEnabled(OneHandedSettingsUtil
-                .getSettingsOneHandedModeEnabled(mContext.getContentResolver()));
-        mTimeoutHandler.setTimeout(OneHandedSettingsUtil
-                .getSettingsOneHandedModeTimeout(mContext.getContentResolver()));
-        mOneHandedController.setTaskChangeToExit(OneHandedSettingsUtil
-                .getSettingsTapsAppToExit(mContext.getContentResolver()));
-        mOneHandedController.setSwipeToNotificationEnabled(OneHandedSettingsUtil
-                .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
-    }
-
-    @Override
-    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher) {
-        if (displayId != DEFAULT_DISPLAY) {
-            return;
-        }
-        if ((vis & InputMethodService.IME_VISIBLE) != 0) {
-            OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
-            stopOneHanded();
-        }
-    }
-
-    @VisibleForTesting
-    private void setEnabledGesturalOverlay(boolean enabled) {
-        try {
-            mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Trigger one handed more
-     */
-    public void startOneHanded() {
-        mOneHandedController.startOneHanded();
-        OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
-    }
-
-    /**
-     * Dismiss one handed more
-     */
-    public void stopOneHanded() {
-        mOneHandedController.stopOneHanded();
-        OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
-    }
-
-    /**
-     * Dump all one handed data of states
-     */
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        final String innerPrefix = "  ";
-        pw.println(TAG + "one handed states: ");
-
-        if (mOneHandedController != null) {
-            mOneHandedController.dump(fd, pw, args);
-        }
-
-        if (mTimeoutHandler != null) {
-            mTimeoutHandler.dump(fd, pw, args);
-        }
-
-        OneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver());
-
-        if (mOverlayManager != null) {
-            OverlayInfo info = null;
-            try {
-                info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY,
-                        USER_CURRENT);
-            } catch (RemoteException e) { /* Do nothing */ }
-
-            if (info != null && !info.isEnabled()) {
-                pw.print(innerPrefix + "OverlayInfo=");
-                pw.println(info);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
deleted file mode 100644
index 38744fe..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.pip;
-
-import android.content.res.Configuration;
-
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-
-import java.io.PrintWriter;
-
-
-public interface BasePipManager {
-    void showPictureInPictureMenu();
-    default void expandPip() {}
-    default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
-    void onConfigurationChanged(Configuration newConfig);
-    default void setShelfHeight(boolean visible, int height) {}
-    default void setPinnedStackAnimationType(int animationType) {}
-    default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {}
-    default void dump(PrintWriter pw) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/Pip.java b/packages/SystemUI/src/com/android/systemui/pip/Pip.java
new file mode 100644
index 0000000..b068370
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/Pip.java
@@ -0,0 +1,170 @@
+/*
+ * 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.pip;
+
+import android.content.res.Configuration;
+import android.media.session.MediaController;
+
+import com.android.systemui.pip.tv.PipController;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
+import java.io.PrintWriter;
+
+/**
+ * Interface to engage picture in picture feature.
+ */
+public interface Pip {
+    /**
+     * Called when showing Pip menu.
+     */
+    void showPictureInPictureMenu();
+
+    /**
+     * Registers {@link com.android.systemui.pip.tv.PipController.Listener} that gets called.
+     * whenever receiving notification on changes in PIP.
+     */
+    default void addListener(PipController.Listener listener) {
+    }
+
+    /**
+     * Registers a {@link com.android.systemui.pip.tv.PipController.MediaListener} to PipController.
+     */
+    default void addMediaListener(PipController.MediaListener listener) {
+    }
+
+    /**
+     * Closes PIP (PIPed activity and PIP system UI).
+     */
+    default void closePip() {
+    }
+
+    /**
+     * Expand PIP, it's possible that specific request to activate the window via Alt-tab.
+     */
+    default void expandPip() {
+    }
+
+    /**
+     * Get current play back state. (e.g: Used in TV)
+     *
+     * @return The state of defined in PipController.
+     */
+    default int getPlaybackState() {
+        return 0;
+    }
+
+    /**
+     * Get MediaController.
+     *
+     * @return The MediaController instance.
+     */
+    default MediaController getMediaController() {
+        return null;
+    }
+
+    /**
+     * Hides the PIP menu.
+     */
+    void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback);
+
+    /**
+     * Returns {@code true} if PIP is shown.
+     */
+    default boolean isPipShown() {
+        return false;
+    }
+
+    /**
+     * Moves the PIPed activity to the fullscreen and closes PIP system UI.
+     */
+    default void movePipToFullscreen() {
+    }
+
+    /**
+     * Called when configuration change invoked.
+     */
+    void onConfigurationChanged(Configuration newConfig);
+
+    /**
+     * Removes a {@link PipController.Listener} from PipController.
+     */
+    default void removeListener(PipController.Listener listener) {
+    }
+
+    /**
+     * Removes a {@link com.android.systemui.pip.tv.PipController.MediaListener} from PipController.
+     */
+    default void removeMediaListener(PipController.MediaListener listener) {
+    }
+
+    /**
+     * Resize the Pip to the appropriate size for the input state.
+     *
+     * @param state In Pip state also used to determine the new size for the Pip.
+     */
+    default void resizePinnedStack(int state) {
+    }
+
+    /**
+     * Resumes resizing operation on the Pip that was previously suspended.
+     *
+     * @param reason The reason resizing operations on the Pip was suspended.
+     */
+    default void resumePipResizing(int reason) {
+    }
+
+    /**
+     * Sets both shelf visibility and its height.
+     *
+     * @param visible visibility of shelf.
+     * @param height  to specify the height for shelf.
+     */
+    default void setShelfHeight(boolean visible, int height) {
+    }
+
+    /**
+     * Set the pinned stack with {@link PipAnimationController.AnimationType}
+     *
+     * @param animationType The pre-defined {@link PipAnimationController.AnimationType}
+     */
+    default void setPinnedStackAnimationType(int animationType) {
+    }
+
+    /**
+     * Registers the pinned stack animation listener.
+     *
+     * @param listener The listener of pinned stack animation.
+     */
+    default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+    }
+
+    /**
+     * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called.
+     *
+     * @param reason The reason for suspending resizing operations on the Pip.
+     */
+    default void suspendPipResizing(int reason) {
+    }
+
+    /**
+     * Dump the current state and information if need.
+     *
+     * @param pw The stream to dump information to.
+     */
+    default void dump(PrintWriter pw) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 7421ec1..5793cdd 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -47,21 +47,25 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Rational;
 import android.util.Size;
 import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowManager;
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 import android.window.WindowContainerTransactionCallback;
-import android.window.WindowOrganizer;
 
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.pip.phone.PipMenuActivityController;
 import com.android.systemui.pip.phone.PipUpdateThread;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -95,6 +99,7 @@
     private static final int MSG_FINISH_RESIZE = 4;
     private static final int MSG_RESIZE_USER = 5;
 
+    private final Context mContext;
     private final Handler mMainHandler;
     private final Handler mUpdateHandler;
     private final PipBoundsHandler mPipBoundsHandler;
@@ -107,6 +112,8 @@
     private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
     private final Optional<SplitScreen> mSplitScreenOptional;
     protected final ShellTaskOrganizer mTaskOrganizer;
+    private SurfaceControlViewHost mPipViewHost;
+    private SurfaceControl mPipMenuSurface;
 
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -212,6 +219,7 @@
             @NonNull DisplayController displayController,
             @NonNull PipUiEventLogger pipUiEventLogger,
             @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+        mContext = context;
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
         mPipBoundsHandler = boundsHandler;
@@ -300,7 +308,7 @@
             // Don't bother doing an animation if the display rotation differs or if it's in
             // a non-supported windowing mode
             applyWindowingModeChangeOnExit(wct, direction);
-            WindowOrganizer.applyTransaction(wct);
+            mTaskOrganizer.applyTransaction(wct);
             // Send finished callback though animation is ignored.
             sendOnPipTransitionFinished(direction);
             mInPip = false;
@@ -370,7 +378,7 @@
             // Reset the task bounds first to ensure the activity configuration is reset as well
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             wct.setBounds(mToken, null);
-            WindowOrganizer.applyTransaction(wct);
+            mTaskOrganizer.applyTransaction(wct);
 
             ActivityTaskManager.getService().removeStacksInWindowingModes(
                     new int[]{ WINDOWING_MODE_PINNED });
@@ -504,6 +512,45 @@
     }
 
     /**
+     * Setup the ViewHost and attach the provided menu view to the ViewHost.
+     */
+    public void attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) {
+        if (mPipMenuSurface != null) {
+            Log.e(TAG, "PIP Menu View already created and attached.");
+            return;
+        }
+
+        if (Looper.getMainLooper() != Looper.myLooper()) {
+            throw new RuntimeException("PipMenuView needs to be attached on the main thread.");
+        }
+
+        mPipViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(),
+                (android.os.Binder) null);
+        mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl();
+        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        transaction.reparent(mPipMenuSurface, mLeash);
+        transaction.show(mPipMenuSurface);
+        transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1);
+        transaction.apply();
+        mPipViewHost.setView(menuView, lp);
+    }
+
+
+    /**
+     * Releases the PIP Menu's View host, remove it from PIP task surface.
+     */
+    public void detachPipMenuViewHost() {
+        if (mPipMenuSurface != null) {
+            SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+            transaction.remove(mPipMenuSurface);
+            transaction.apply();
+            mPipMenuSurface = null;
+            mPipViewHost = null;
+        }
+    }
+
+
+    /**
      * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
      * Meanwhile this callback is invoked whenever the task is removed. For instance:
      *   - as a result of removeStacksInWindowingModes from WM
@@ -637,13 +684,16 @@
      * {@link PictureInPictureParams} would affect the bounds.
      */
     private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
-        final boolean changed = (mPictureInPictureParams == null) || !Objects.equals(
-                mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational());
-        if (changed) {
-            mPictureInPictureParams = params;
+        final Rational currentAspectRatio =
+                mPictureInPictureParams != null ? mPictureInPictureParams.getAspectRatioRational()
+                        : null;
+        final boolean aspectRatioChanged = !Objects.equals(currentAspectRatio,
+                params.getAspectRatioRational());
+        mPictureInPictureParams = params;
+        if (aspectRatioChanged) {
             mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
         }
-        return changed;
+        return aspectRatioChanged;
     }
 
     /**
@@ -838,6 +888,12 @@
         WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareFinishResizeTransaction(destinationBounds, direction, tx, wct);
         applyFinishBoundsResize(wct, direction);
+        runOnMainHandler(() -> {
+            if (mPipViewHost != null) {
+                mPipViewHost.relayout(PipMenuActivityController.getPipMenuLayoutParams(
+                        destinationBounds.width(), destinationBounds.height()));
+            }
+        });
     }
 
     private void prepareFinishResizeTransaction(Rect destinationBounds,
@@ -875,7 +931,7 @@
      */
     public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
             @PipAnimationController.TransitionDirection int direction) {
-        WindowOrganizer.applyTransaction(wct);
+        mTaskOrganizer.applyTransaction(wct);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
deleted file mode 100644
index 2cd1e20..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2016 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.pip;
-
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.os.UserHandle;
-import android.os.UserManager;
-
-import com.android.systemui.SystemUI;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.statusbar.CommandQueue;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-import javax.inject.Inject;
-
-/**
- * Controls the picture-in-picture window.
- */
-@SysUISingleton
-public class PipUI extends SystemUI implements CommandQueue.Callbacks {
-
-    private final CommandQueue mCommandQueue;
-    private BasePipManager mPipManager;
-
-    @Inject
-    public PipUI(Context context, CommandQueue commandQueue,
-            BasePipManager pipManager) {
-        super(context);
-        mCommandQueue = commandQueue;
-        mPipManager = pipManager;
-    }
-
-    @Override
-    public void start() {
-        PackageManager pm = mContext.getPackageManager();
-        boolean supportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
-        if (!supportsPip) {
-            return;
-        }
-
-        // Ensure that we are the primary user's SystemUI.
-        final int processUser = UserManager.get(mContext).getUserHandle();
-        if (processUser != UserHandle.USER_SYSTEM) {
-            throw new IllegalStateException("Non-primary Pip component not currently supported.");
-        }
-
-        mCommandQueue.addCallback(this);
-    }
-
-    @Override
-    public void showPictureInPictureMenu() {
-        mPipManager.showPictureInPictureMenu();
-    }
-
-    public void expandPip() {
-        mPipManager.expandPip();
-    }
-
-    public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
-        mPipManager.hidePipMenu(onStartCallback, onEndCallback);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (mPipManager == null) {
-            return;
-        }
-
-        mPipManager.onConfigurationChanged(newConfig);
-    }
-
-    public void setShelfHeight(boolean visible, int height) {
-        if (mPipManager == null) {
-            return;
-        }
-
-        mPipManager.setShelfHeight(visible, height);
-    }
-
-    public void setPinnedStackAnimationType(int animationType) {
-        if (mPipManager != null) {
-            mPipManager.setPinnedStackAnimationType(animationType);
-        }
-    }
-
-    public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
-        if (mPipManager != null) {
-            mPipManager.setPinnedStackAnimationListener(listener);
-        }
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mPipManager == null) {
-            return;
-        }
-
-        mPipManager.dump(pw);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
rename to packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
index d8864ec..6998e90 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
 import static com.android.systemui.pip.PipAnimationController.isOutPipDirection;
 
@@ -28,11 +29,14 @@
 import android.app.RemoteAction;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 import android.util.Pair;
 import android.view.DisplayInfo;
@@ -44,37 +48,31 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.model.SysUiState;
-import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.Pip;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSurfaceTransactionHelper;
 import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.FloatingContentCoordinator;
-import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 
 import java.io.PrintWriter;
-import java.util.Optional;
-
-import javax.inject.Inject;
 
 /**
  * Manages the picture-in-picture (PIP) UI and states for Phones.
  */
 @SysUISingleton
-public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
-    private static final String TAG = "PipManager";
+public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback {
+    private static final String TAG = "PipController";
 
     private Context mContext;
     private IActivityManager mActivityManager;
@@ -88,15 +86,15 @@
     private DisplayController mDisplayController;
     private InputConsumerController mInputConsumerController;
     private PipAppOpsListener mAppOpsListener;
+    private PipBoundsHandler mPipBoundsHandler;
     private PipMediaController mMediaController;
     private PipTouchHandler mTouchHandler;
-    private PipTaskOrganizer mPipTaskOrganizer;
     private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
     private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener;
     private boolean mIsInFixedRotation;
 
-    protected PipBoundsHandler mPipBoundsHandler;
     protected PipMenuActivityController mMenuController;
+    protected PipTaskOrganizer mPipTaskOrganizer;
 
     /**
      * Handler for display rotation changes.
@@ -128,7 +126,7 @@
             // not during the fixed rotation. In fixed rotation case, app is about to enter PiP
             // and we need the offsets preserved to calculate the destination bounds.
             if (!mIsInFixedRotation) {
-                mPipBoundsHandler.setShelfHeight(false , 0);
+                mPipBoundsHandler.setShelfHeight(false, 0);
                 mPipBoundsHandler.onImeVisibilityChanged(false, 0);
                 mTouchHandler.onShelfVisibilityChanged(false, 0);
                 mTouchHandler.onImeVisibilityChanged(false, 0);
@@ -202,7 +200,7 @@
     /**
      * Handler for messages from the PIP controller.
      */
-    private class PipManagerPinnedStackListener extends PinnedStackListener {
+    private class PipControllerPinnedStackListener extends PinnedStackListener {
         @Override
         public void onListenerRegistered(IPinnedStackController controller) {
             mHandler.post(() -> mTouchHandler.setPinnedStackController(controller));
@@ -254,50 +252,61 @@
 
     public ConfigurationController.ConfigurationListener mOverlayChangedListener =
             new ConfigurationController.ConfigurationListener() {
-        @Override
-        public void onOverlayChanged() {
-            mHandler.post(() -> {
-                mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
-                updateMovementBounds(null /* toBounds */,
-                        false /* fromRotation */, false /* fromImeAdjustment */,
-                        false /* fromShelfAdjustment */, null /* windowContainerTransaction */);
-            });
-        }
-    };
+                @Override
+                public void onOverlayChanged() {
+                    mHandler.post(() -> {
+                        mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
+                        updateMovementBounds(null /* toBounds */,
+                                false /* fromRotation */, false /* fromImeAdjustment */,
+                                false /* fromShelfAdjustment */,
+                                null /* windowContainerTransaction */);
+                    });
+                }
+            };
 
-    @Inject
-    public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
-            @PipMenuActivityClass Class<?> pipMenuActivityClass,
+    public PipController(Context context, BroadcastDispatcher broadcastDispatcher,
             ConfigurationController configController,
             DeviceConfigProxy deviceConfig,
             DisplayController displayController,
-            Optional<SplitScreen> splitScreenOptional,
             FloatingContentCoordinator floatingContentCoordinator,
             SysUiState sysUiState,
-            PipUiEventLogger pipUiEventLogger,
-            ShellTaskOrganizer shellTaskOrganizer) {
+            PipBoundsHandler pipBoundsHandler,
+            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+            PipTaskOrganizer pipTaskOrganizer,
+            PipUiEventLogger pipUiEventLogger) {
         mContext = context;
         mActivityManager = ActivityManager.getService();
 
+        PackageManager pm = context.getPackageManager();
+        boolean supportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+        if (!supportsPip) {
+            Log.w(TAG, "Device not support PIP feature");
+            return;
+        }
+
+        // Ensure that we are the primary user's SystemUI.
+        final int processUser = UserManager.get(context).getUserHandle();
+        if (processUser != UserHandle.USER_SYSTEM) {
+            throw new IllegalStateException("Non-primary Pip component not currently supported.");
+        }
+
         try {
             WindowManagerWrapper.getInstance().addPinnedStackListener(
-                    new PipManagerPinnedStackListener());
+                    new PipControllerPinnedStackListener());
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
 
         mDisplayController = displayController;
-        mPipBoundsHandler = new PipBoundsHandler(mContext);
-        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController);
-        mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
-                mPipSurfaceTransactionHelper, splitScreenOptional, mDisplayController,
-                pipUiEventLogger, shellTaskOrganizer);
+        mPipBoundsHandler = pipBoundsHandler;
+        mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
+        mPipTaskOrganizer = pipTaskOrganizer;
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mInputConsumerController = InputConsumerController.getPipInputConsumer();
         mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
-        mMenuController = new PipMenuActivityController(context, pipMenuActivityClass,
-                mMediaController, mInputConsumerController);
+        mMenuController = new PipMenuActivityController(context,
+                mMediaController, mInputConsumerController, mPipTaskOrganizer);
         mTouchHandler = new PipTouchHandler(context, mActivityManager,
                 mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
                 floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger);
@@ -452,6 +461,7 @@
                 mTmpDisplayInfo.rotation);
     }
 
+    @Override
     public void dump(PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
deleted file mode 100644
index 1b1b2de..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Copyright (C) 2016 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.pip.phone;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_DISMISS_FRACTION;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MENU_STATE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU_WITH_DELAY;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_RESIZE_HANDLE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_WILL_RESIZE_MENU;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ParceledListSlice;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager.LayoutParams;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-
-import com.android.systemui.Interpolators;
-import com.android.wm.shell.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
- * TODO(b/150319024): PipMenuActivity will move to a Window
- */
-public class PipMenuActivity extends Activity {
-
-    private static final String TAG = "PipMenuActivity";
-
-    private static final int MESSAGE_INVALID_TYPE = -1;
-
-    public static final int MESSAGE_SHOW_MENU = 1;
-    public static final int MESSAGE_POKE_MENU = 2;
-    public static final int MESSAGE_HIDE_MENU = 3;
-    public static final int MESSAGE_UPDATE_ACTIONS = 4;
-    public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
-    public static final int MESSAGE_ANIMATION_ENDED = 6;
-    public static final int MESSAGE_POINTER_EVENT = 7;
-    public static final int MESSAGE_MENU_EXPANDED = 8;
-    public static final int MESSAGE_FADE_OUT_MENU = 9;
-    public static final int MESSAGE_UPDATE_MENU_LAYOUT = 10;
-
-    private static final int INITIAL_DISMISS_DELAY = 3500;
-    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
-    private static final long MENU_FADE_DURATION = 125;
-    private static final long MENU_SLOW_FADE_DURATION = 175;
-    private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
-
-    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
-    private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
-
-    private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
-    private static final boolean ENABLE_RESIZE_HANDLE = false;
-
-    private int mMenuState;
-    private boolean mResize = true;
-    private boolean mAllowMenuTimeout = true;
-    private boolean mAllowTouches = true;
-
-    private final List<RemoteAction> mActions = new ArrayList<>();
-
-    private AccessibilityManager mAccessibilityManager;
-    private Drawable mBackgroundDrawable;
-    private View mMenuContainer;
-    private LinearLayout mActionsGroup;
-    private int mBetweenActionPaddingLand;
-
-    private AnimatorSet mMenuContainerAnimator;
-
-    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float alpha = (float) animation.getAnimatedValue();
-                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
-                }
-            };
-
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_SHOW_MENU: {
-                    final Bundle data = (Bundle) msg.obj;
-                    showMenu(data.getInt(EXTRA_MENU_STATE),
-                            data.getParcelable(EXTRA_STACK_BOUNDS),
-                            data.getBoolean(EXTRA_ALLOW_TIMEOUT),
-                            data.getBoolean(EXTRA_WILL_RESIZE_MENU),
-                            data.getBoolean(EXTRA_SHOW_MENU_WITH_DELAY),
-                            data.getBoolean(EXTRA_SHOW_RESIZE_HANDLE));
-                    break;
-                }
-                case MESSAGE_POKE_MENU:
-                    cancelDelayedFinish();
-                    break;
-                case MESSAGE_HIDE_MENU:
-                    hideMenu((Runnable) msg.obj);
-                    break;
-                case MESSAGE_UPDATE_ACTIONS: {
-                    final Bundle data = (Bundle) msg.obj;
-                    final ParceledListSlice<RemoteAction> actions = data.getParcelable(
-                            EXTRA_ACTIONS);
-                    setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
-                            ? actions.getList() : Collections.emptyList());
-                    break;
-                }
-                case MESSAGE_UPDATE_DISMISS_FRACTION: {
-                    final Bundle data = (Bundle) msg.obj;
-                    updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION));
-                    break;
-                }
-                case MESSAGE_ANIMATION_ENDED: {
-                    mAllowTouches = true;
-                    break;
-                }
-                case MESSAGE_POINTER_EVENT: {
-                    final MotionEvent ev = (MotionEvent) msg.obj;
-                    dispatchPointerEvent(ev);
-                    break;
-                }
-                case MESSAGE_MENU_EXPANDED : {
-                    if (mMenuContainerAnimator == null) {
-                        return;
-                    }
-                    mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY);
-                    mMenuContainerAnimator.start();
-                    break;
-                }
-                case MESSAGE_FADE_OUT_MENU: {
-                    fadeOutMenu();
-                    break;
-                }
-                case MESSAGE_UPDATE_MENU_LAYOUT: {
-                    if (mPipMenuIconsAlgorithm == null) {
-                        return;
-                    }
-                    final Rect bounds = (Rect) msg.obj;
-                    mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
-                    break;
-                }
-            }
-        }
-    };
-    private Messenger mToControllerMessenger;
-    private Messenger mMessenger = new Messenger(mHandler);
-
-    private final Runnable mFinishRunnable = this::hideMenu;
-
-    protected View mViewRoot;
-    protected View mSettingsButton;
-    protected View mDismissButton;
-    protected View mResizeHandle;
-    protected View mTopEndContainer;
-    protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        // Set the flags to allow us to watch for outside touches and also hide the menu and start
-        // manipulating the PIP in the same touch gesture
-        getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.pip_menu_activity);
-
-        mAccessibilityManager = getSystemService(AccessibilityManager.class);
-        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
-        mBackgroundDrawable.setAlpha(0);
-        mViewRoot = findViewById(R.id.background);
-        mViewRoot.setBackground(mBackgroundDrawable);
-        mMenuContainer = findViewById(R.id.menu_container);
-        mMenuContainer.setAlpha(0);
-        mTopEndContainer = findViewById(R.id.top_end_container);
-        mSettingsButton = findViewById(R.id.settings);
-        mSettingsButton.setAlpha(0);
-        mSettingsButton.setOnClickListener((v) -> {
-            if (v.getAlpha() != 0) {
-                showSettings();
-            }
-        });
-        mDismissButton = findViewById(R.id.dismiss);
-        mDismissButton.setAlpha(0);
-        mDismissButton.setOnClickListener(v -> dismissPip());
-        findViewById(R.id.expand_button).setOnClickListener(v -> {
-            if (mMenuContainer.getAlpha() != 0) {
-                expandPip();
-            }
-        });
-        mResizeHandle = findViewById(R.id.resize_handle);
-        mResizeHandle.setAlpha(0);
-        mActionsGroup = findViewById(R.id.actions_group);
-        mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
-                R.dimen.pip_between_action_padding_land);
-        mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(this.getApplicationContext());
-        mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
-                mResizeHandle, mSettingsButton, mDismissButton);
-        updateFromIntent(getIntent());
-        setTitle(R.string.pip_menu_title);
-        setDisablePreviewScreenshots(true);
-
-        // Hide without an animation.
-        getWindow().setExitTransition(null);
-
-        initAccessibility();
-    }
-
-    private void initAccessibility() {
-        getWindow().getDecorView().setAccessibilityDelegate(new View.AccessibilityDelegate() {
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-                String label = getResources().getString(R.string.pip_menu_title);
-                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label));
-            }
-
-            @Override
-            public boolean performAccessibilityAction(View host, int action, Bundle args) {
-                if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
-                    Message m = Message.obtain();
-                    m.what = PipMenuActivityController.MESSAGE_SHOW_MENU;
-                    sendMessage(m, "Could not notify controller to show PIP menu");
-                }
-                return super.performAccessibilityAction(host, action, args);
-            }
-        });
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
-            hideMenu();
-            return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        updateFromIntent(intent);
-    }
-
-    @Override
-    public void onUserInteraction() {
-        if (mAllowMenuTimeout) {
-            repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
-        }
-    }
-
-    @Override
-    protected void onUserLeaveHint() {
-        super.onUserLeaveHint();
-
-        // If another task is starting on top of the menu, then hide and finish it so that it can be
-        // recreated on the top next time it starts
-        hideMenu();
-    }
-
-    @Override
-    public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
-        super.onTopResumedActivityChanged(isTopResumedActivity);
-        if (!isTopResumedActivity && mMenuState != MENU_STATE_NONE) {
-            hideMenu();
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // In cases such as device lock, hide and finish it so that it can be recreated on the top
-        // next time it starts, see also {@link #onUserLeaveHint}
-        hideMenu();
-        cancelDelayedFinish();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        // Fallback, if we are destroyed for any other reason (like when the task is being reset),
-        // also reset the callback.
-        notifyActivityCallback(null);
-    }
-
-    @Override
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        if (!isInPictureInPictureMode) {
-            finish();
-        }
-    }
-
-    /**
-     * Dispatch a pointer event from {@link PipTouchHandler}.
-     */
-    private void dispatchPointerEvent(MotionEvent event) {
-        if (event.isTouchEvent()) {
-            dispatchTouchEvent(event);
-        } else {
-            dispatchGenericMotionEvent(event);
-        }
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (!mAllowTouches) {
-            return false;
-        }
-
-        // On the first action outside the window, hide the menu
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_OUTSIDE:
-                hideMenu();
-                return true;
-        }
-        return super.dispatchTouchEvent(ev);
-    }
-
-    @Override
-    public void finish() {
-        notifyActivityCallback(null);
-        super.finish();
-    }
-
-    @Override
-    public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
-        // Do nothing
-    }
-
-    private void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
-            boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
-        mAllowMenuTimeout = allowMenuTimeout;
-        if (mMenuState != menuState) {
-            // Disallow touches if the menu needs to resize while showing, and we are transitioning
-            // to/from a full menu state.
-            boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
-                    (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
-            mAllowTouches = !disallowTouchesUntilAnimationEnd;
-            cancelDelayedFinish();
-            updateActionViews(stackBounds);
-            if (mMenuContainerAnimator != null) {
-                mMenuContainerAnimator.cancel();
-            }
-            mMenuContainerAnimator = new AnimatorSet();
-            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
-                    mMenuContainer.getAlpha(), 1f);
-            menuAnim.addUpdateListener(mMenuBgUpdateListener);
-            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
-                    mSettingsButton.getAlpha(), 1f);
-            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
-                    mDismissButton.getAlpha(), 1f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(),
-                    ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
-                            ? 1f : 0f);
-            if (menuState == MENU_STATE_FULL) {
-                mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
-                        resizeAnim);
-            } else {
-                mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
-            }
-            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
-            mMenuContainerAnimator.setDuration(menuState == MENU_STATE_CLOSE
-                    ? MENU_FADE_DURATION
-                    : MENU_SLOW_FADE_DURATION);
-            if (allowMenuTimeout) {
-                mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        repostDelayedFinish(INITIAL_DISMISS_DELAY);
-                    }
-                });
-            }
-            if (withDelay) {
-                // starts the menu container animation after window expansion is completed
-                notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_MENU_EXPANDED);
-            } else {
-                notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_INVALID_TYPE);
-                mMenuContainerAnimator.start();
-            }
-        } else {
-            // If we are already visible, then just start the delayed dismiss and unregister any
-            // existing input consumers from the previous drag
-            if (allowMenuTimeout) {
-                repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
-            }
-        }
-    }
-
-    /**
-     * Different from {@link #hideMenu()}, this function does not try to finish this menu activity
-     * and instead, it fades out the controls by setting the alpha to 0 directly without menu
-     * visibility callbacks invoked.
-     */
-    private void fadeOutMenu() {
-        mMenuContainer.setAlpha(0f);
-        mSettingsButton.setAlpha(0f);
-        mDismissButton.setAlpha(0f);
-        mResizeHandle.setAlpha(0f);
-    }
-
-    private void hideMenu() {
-        hideMenu(null);
-    }
-
-    private void hideMenu(Runnable animationEndCallback) {
-        hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */,
-                true /* animate */);
-    }
-
-    private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
-            boolean isDismissing, boolean animate) {
-        if (mMenuState != MENU_STATE_NONE) {
-            cancelDelayedFinish();
-            if (notifyMenuVisibility) {
-                notifyMenuStateChange(MENU_STATE_NONE, mResize, MESSAGE_INVALID_TYPE);
-            }
-            mMenuContainerAnimator = new AnimatorSet();
-            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
-                    mMenuContainer.getAlpha(), 0f);
-            menuAnim.addUpdateListener(mMenuBgUpdateListener);
-            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
-                    mSettingsButton.getAlpha(), 0f);
-            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
-                    mDismissButton.getAlpha(), 0f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(), 0f);
-            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
-            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
-            mMenuContainerAnimator.setDuration(animate ? MENU_FADE_DURATION : 0);
-            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (animationFinishedRunnable != null) {
-                        animationFinishedRunnable.run();
-                    }
-
-                    if (!isDismissing) {
-                        // If we are dismissing the PiP, then don't try to pre-emptively finish the
-                        // menu activity
-                        finish();
-                    }
-                }
-            });
-            mMenuContainerAnimator.start();
-        } else {
-            // If the menu is not visible, just finish now
-            finish();
-        }
-    }
-
-    private void updateFromIntent(Intent intent) {
-        mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
-        if (mToControllerMessenger == null) {
-            Log.w(TAG, "Controller messenger is null. Stopping.");
-            finish();
-            return;
-        }
-        notifyActivityCallback(mMessenger);
-
-        ParceledListSlice<RemoteAction> actions = intent.getParcelableExtra(EXTRA_ACTIONS);
-        if (actions != null) {
-            mActions.clear();
-            mActions.addAll(actions.getList());
-        }
-
-        final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
-        if (menuState != MENU_STATE_NONE) {
-            Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
-            boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
-            boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
-            boolean withDelay = intent.getBooleanExtra(EXTRA_SHOW_MENU_WITH_DELAY, false);
-            boolean showResizeHandle = intent.getBooleanExtra(EXTRA_SHOW_RESIZE_HANDLE, false);
-            showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
-                    showResizeHandle);
-        }
-    }
-
-    private void setActions(Rect stackBounds, List<RemoteAction> actions) {
-        mActions.clear();
-        mActions.addAll(actions);
-        updateActionViews(stackBounds);
-    }
-
-    private void updateActionViews(Rect stackBounds) {
-        ViewGroup expandContainer = findViewById(R.id.expand_container);
-        ViewGroup actionsContainer = findViewById(R.id.actions_container);
-        actionsContainer.setOnTouchListener((v, ev) -> {
-            // Do nothing, prevent click through to parent
-            return true;
-        });
-
-        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
-            actionsContainer.setVisibility(View.INVISIBLE);
-        } else {
-            actionsContainer.setVisibility(View.VISIBLE);
-            if (mActionsGroup != null) {
-                // Ensure we have as many buttons as actions
-                final LayoutInflater inflater = LayoutInflater.from(this);
-                while (mActionsGroup.getChildCount() < mActions.size()) {
-                    final ImageButton actionView = (ImageButton) inflater.inflate(
-                            R.layout.pip_menu_action, mActionsGroup, false);
-                    mActionsGroup.addView(actionView);
-                }
-
-                // Update the visibility of all views
-                for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
-                    mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
-                            ? View.VISIBLE
-                            : View.GONE);
-                }
-
-                // Recreate the layout
-                final boolean isLandscapePip = stackBounds != null &&
-                        (stackBounds.width() > stackBounds.height());
-                for (int i = 0; i < mActions.size(); i++) {
-                    final RemoteAction action = mActions.get(i);
-                    final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
-
-                    // TODO: Check if the action drawable has changed before we reload it
-                    action.getIcon().loadDrawableAsync(this, d -> {
-                        d.setTint(Color.WHITE);
-                        actionView.setImageDrawable(d);
-                    }, mHandler);
-                    actionView.setContentDescription(action.getContentDescription());
-                    if (action.isEnabled()) {
-                        actionView.setOnClickListener(v -> {
-                            mHandler.post(() -> {
-                                try {
-                                    action.getActionIntent().send();
-                                } catch (CanceledException e) {
-                                    Log.w(TAG, "Failed to send action", e);
-                                }
-                            });
-                        });
-                    }
-                    actionView.setEnabled(action.isEnabled());
-                    actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
-
-                    // Update the margin between actions
-                    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
-                            actionView.getLayoutParams();
-                    lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
-                }
-            }
-
-            // Update the expand container margin to adjust the center of the expand button to
-            // account for the existence of the action container
-            FrameLayout.LayoutParams expandedLp =
-                    (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
-            expandedLp.topMargin = getResources().getDimensionPixelSize(
-                    R.dimen.pip_action_padding);
-            expandedLp.bottomMargin = getResources().getDimensionPixelSize(
-                    R.dimen.pip_expand_container_edge_margin);
-            expandContainer.requestLayout();
-        }
-    }
-
-    private void updateDismissFraction(float fraction) {
-        int alpha;
-        final float menuAlpha = 1 - fraction;
-        if (mMenuState == MENU_STATE_FULL) {
-            mMenuContainer.setAlpha(menuAlpha);
-            mSettingsButton.setAlpha(menuAlpha);
-            mDismissButton.setAlpha(menuAlpha);
-            final float interpolatedAlpha =
-                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
-            alpha = (int) (interpolatedAlpha * 255);
-        } else {
-            if (mMenuState == MENU_STATE_CLOSE) {
-                mDismissButton.setAlpha(menuAlpha);
-            }
-            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
-        }
-        mBackgroundDrawable.setAlpha(alpha);
-    }
-
-    private void notifyMenuStateChange(int menuState, boolean resize, int callbackWhat) {
-        mMenuState = menuState;
-        mResize = resize;
-        Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
-        m.arg1 = menuState;
-        m.arg2 = resize ? 1 : 0;
-        if (callbackWhat != MESSAGE_INVALID_TYPE) {
-            // This message could be sent across processes when in secondary user.
-            // Make the receiver end sending back via our own Messenger
-            m.replyTo = mMessenger;
-            final Bundle data = new Bundle(1);
-            data.putInt(PipMenuActivityController.EXTRA_MESSAGE_CALLBACK_WHAT, callbackWhat);
-            m.obj = data;
-        }
-        sendMessage(m, "Could not notify controller of PIP menu visibility");
-    }
-
-    private void expandPip() {
-        // Do not notify menu visibility when hiding the menu, the controller will do this when it
-        // handles the message
-        hideMenu(() -> {
-            sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
-                    "Could not notify controller to expand PIP");
-        }, false /* notifyMenuVisibility */, false /* isDismissing */, true /* animate */);
-    }
-
-    private void dismissPip() {
-        // Since tapping on the close-button invokes a double-tap wait callback in PipTouchHandler,
-        // we want to disable animating the fadeout animation of the buttons in order to call on
-        // PipTouchHandler#onPipDismiss fast enough.
-        final boolean animate = mMenuState != MENU_STATE_CLOSE;
-        // Do not notify menu visibility when hiding the menu, the controller will do this when it
-        // handles the message
-        hideMenu(() -> {
-            sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
-                    "Could not notify controller to dismiss PIP");
-        }, false /* notifyMenuVisibility */, true /* isDismissing */, animate);
-    }
-
-    private void showSettings() {
-        final Pair<ComponentName, Integer> topPipActivityInfo =
-                PipUtils.getTopPipActivity(this, ActivityManager.getService());
-        if (topPipActivityInfo.first != null) {
-            final UserHandle user = UserHandle.of(topPipActivityInfo.second);
-            final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
-                    Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
-            settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
-            settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
-            startActivity(settingsIntent);
-        }
-    }
-
-    private void notifyActivityCallback(Messenger callback) {
-        Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
-        m.replyTo = callback;
-        m.arg1 = mResize ?  1 : 0;
-        sendMessage(m, "Could not notify controller of activity finished");
-    }
-
-    private void sendEmptyMessage(int what, String errorMsg) {
-        Message m = Message.obtain();
-        m.what = what;
-        sendMessage(m, errorMsg);
-    }
-
-    private void sendMessage(Message m, String errorMsg) {
-        if (mToControllerMessenger == null) {
-            return;
-        }
-        try {
-            mToControllerMessenger.send(m);
-        } catch (RemoteException e) {
-            Log.e(TAG, errorMsg, e);
-        }
-    }
-
-    private void cancelDelayedFinish() {
-        mHandler.removeCallbacks(mFinishRunnable);
-    }
-
-    private void repostDelayedFinish(int delay) {
-        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
-                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
-        mHandler.removeCallbacks(mFinishRunnable);
-        mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 383f6b3..873ba26 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -19,27 +19,20 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager.StackInfo;
-import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.RemoteAction;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.os.Debug;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.WindowManager;
 
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.pip.phone.PipMediaController.ActionListener;
 import com.android.systemui.shared.system.InputConsumerController;
 
@@ -58,30 +51,10 @@
     private static final String TAG = "PipMenuActController";
     private static final boolean DEBUG = false;
 
-    public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
-    public static final String EXTRA_ACTIONS = "actions";
-    public static final String EXTRA_STACK_BOUNDS = "stack_bounds";
-    public static final String EXTRA_ALLOW_TIMEOUT = "allow_timeout";
-    public static final String EXTRA_WILL_RESIZE_MENU = "resize_menu_on_show";
-    public static final String EXTRA_DISMISS_FRACTION = "dismiss_fraction";
-    public static final String EXTRA_MENU_STATE = "menu_state";
-    public static final String EXTRA_SHOW_MENU_WITH_DELAY = "show_menu_with_delay";
-    public static final String EXTRA_SHOW_RESIZE_HANDLE = "show_resize_handle";
-    public static final String EXTRA_MESSAGE_CALLBACK_WHAT = "message_callback_what";
-
-    public static final int MESSAGE_MENU_STATE_CHANGED = 100;
-    public static final int MESSAGE_EXPAND_PIP = 101;
-    public static final int MESSAGE_DISMISS_PIP = 103;
-    public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
-    public static final int MESSAGE_SHOW_MENU = 107;
-
     public static final int MENU_STATE_NONE = 0;
     public static final int MENU_STATE_CLOSE = 1;
     public static final int MENU_STATE_FULL = 2;
 
-    // The duration to wait before we consider the start activity as having timed out
-    private static final long START_ACTIVITY_REQUEST_TIMEOUT_MS = 300;
-
     /**
      * A listener interface to receive notification on changes in PIP.
      */
@@ -110,9 +83,8 @@
         void onPipShowMenu();
     }
 
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
-    private Class<?> mPipMenuActivityClass;
     private Context mContext;
+    private PipTaskOrganizer mPipTaskOrganizer;
     private PipMediaController mMediaController;
     private InputConsumerController mInputConsumerController;
 
@@ -121,63 +93,7 @@
     private ParceledListSlice<RemoteAction> mMediaActions;
     private int mMenuState;
 
-    // The dismiss fraction update is sent frequently, so use a temporary bundle for the message
-    private Bundle mTmpDismissFractionData = new Bundle();
-
-    private Runnable mOnAnimationEndRunnable;
-    private boolean mStartActivityRequested;
-    private long mStartActivityRequestedTime;
-    private Messenger mToActivityMessenger;
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_MENU_STATE_CHANGED: {
-                    final int menuState = msg.arg1;
-                    final boolean resize = msg.arg2 != 0;
-                    onMenuStateChanged(menuState, resize,
-                            getMenuStateChangeFinishedCallback(msg.replyTo, (Bundle) msg.obj));
-                    break;
-                }
-                case MESSAGE_EXPAND_PIP: {
-                    mListeners.forEach(Listener::onPipExpand);
-                    break;
-                }
-                case MESSAGE_DISMISS_PIP: {
-                    mListeners.forEach(Listener::onPipDismiss);
-                    break;
-                }
-                case MESSAGE_SHOW_MENU: {
-                    mListeners.forEach(Listener::onPipShowMenu);
-                    break;
-                }
-                case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
-                    mToActivityMessenger = msg.replyTo;
-                    setStartActivityRequested(false);
-                    if (mOnAnimationEndRunnable != null) {
-                        mOnAnimationEndRunnable.run();
-                        mOnAnimationEndRunnable = null;
-                    }
-                    // Mark the menu as invisible once the activity finishes as well
-                    if (mToActivityMessenger == null) {
-                        final boolean resize = msg.arg1 != 0;
-                        onMenuStateChanged(MENU_STATE_NONE, resize, null /* callback */);
-                    }
-                    break;
-                }
-            }
-        }
-    };
-    private Messenger mMessenger = new Messenger(mHandler);
-
-    private Runnable mStartActivityRequestedTimeoutRunnable = () -> {
-        setStartActivityRequested(false);
-        if (mOnAnimationEndRunnable != null) {
-            mOnAnimationEndRunnable.run();
-            mOnAnimationEndRunnable = null;
-        }
-        Log.e(TAG, "Expected start menu activity request timed out");
-    };
+    private PipMenuView mPipMenuView;
 
     private ActionListener mMediaActionListener = new ActionListener() {
         @Override
@@ -187,39 +103,40 @@
         }
     };
 
-    public PipMenuActivityController(Context context, Class<?> pipMenuActivityClass,
-            PipMediaController mediaController, InputConsumerController inputConsumerController
+    public PipMenuActivityController(Context context,
+            PipMediaController mediaController, InputConsumerController inputConsumerController,
+            PipTaskOrganizer pipTaskOrganizer
     ) {
         mContext = context;
         mMediaController = mediaController;
         mInputConsumerController = inputConsumerController;
-        mPipMenuActivityClass = pipMenuActivityClass;
+        mPipTaskOrganizer = pipTaskOrganizer;
     }
 
-    public boolean isMenuActivityVisible() {
-        return mToActivityMessenger != null;
+    public boolean isMenuVisible() {
+        return mPipMenuView != null && mMenuState != MENU_STATE_NONE;
     }
 
     public void onActivityPinned() {
+        if (mPipMenuView == null) {
+            WindowManager.LayoutParams lp =
+                    getPipMenuLayoutParams(0, 0);
+            mPipMenuView = new PipMenuView(mContext, this);
+            mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, lp);
+        }
         mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
     }
 
     public void onActivityUnpinned() {
         hideMenu();
         mInputConsumerController.unregisterInputConsumer();
-        setStartActivityRequested(false);
+        mPipTaskOrganizer.detachPipMenuViewHost();
+        mPipMenuView = null;
     }
 
     public void onPinnedStackAnimationEnded() {
-        // Note: Only active menu activities care about this event
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_ANIMATION_ENDED;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu pinned animation ended", e);
-            }
+        if (isMenuVisible()) {
+            mPipMenuView.onPipAnimationEnded();
         }
     }
 
@@ -236,27 +153,13 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     public void setDismissFraction(float fraction) {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "setDismissFraction() hasActivity=" + (mToActivityMessenger != null)
+            Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible
                     + " fraction=" + fraction);
         }
-        if (mToActivityMessenger != null) {
-            mTmpDismissFractionData.clear();
-            mTmpDismissFractionData.putFloat(EXTRA_DISMISS_FRACTION, fraction);
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION;
-            m.obj = mTmpDismissFractionData;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to update dismiss fraction", e);
-            }
-        } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) {
-            // If we haven't requested the start activity, or if it previously took too long to
-            // start, then start it
-            startMenuActivity(MENU_STATE_NONE, null /* stackBounds */,
-                    false /* allowMenuTimeout */, false /* resizeMenuOnShow */,
-                    false /* withDelay */, false /* showResizeHandle */);
+        if (isMenuVisible) {
+            mPipMenuView.updateDismissFraction(fraction);
         }
     }
 
@@ -282,27 +185,11 @@
                 false /* withDelay */, showResizeHandle);
     }
 
-    private Runnable getMenuStateChangeFinishedCallback(@Nullable Messenger replyTo,
-            @Nullable Bundle data) {
-        if (replyTo == null || data == null) {
-            return null;
-        }
-        return () -> {
-            try {
-                final Message m = Message.obtain();
-                m.what = data.getInt(EXTRA_MESSAGE_CALLBACK_WHAT);
-                replyTo.send(m);
-            } catch (RemoteException e) {
-                // ignored
-            }
-        };
-    }
-
     private void showMenuInternal(int menuState, Rect stackBounds, boolean allowMenuTimeout,
             boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) {
         if (DEBUG) {
             Log.d(TAG, "showMenu() state=" + menuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
+                    + " isMenuVisible=" + isMenuVisible()
                     + " allowMenuTimeout=" + allowMenuTimeout
                     + " willResizeMenu=" + willResizeMenu
                     + " withDelay=" + withDelay
@@ -310,64 +197,34 @@
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
 
-        if (mToActivityMessenger != null) {
-            Bundle data = new Bundle();
-            data.putInt(EXTRA_MENU_STATE, menuState);
-            if (stackBounds != null) {
-                data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
-            }
-            data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
-            data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
-            data.putBoolean(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
-            data.putBoolean(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle);
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_SHOW_MENU;
-            m.obj = data;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to show", e);
-            }
-        } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) {
-            // If we haven't requested the start activity, or if it previously took too long to
-            // start, then start it
-            startMenuActivity(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
-                    showResizeHandle);
+        if (mPipMenuView == null) {
+            Log.e(TAG, "PipMenu has not been attached yet.");
+            return;
         }
+        mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
+                showResizeHandle);
     }
 
     /**
      * Pokes the menu, indicating that the user is interacting with it.
      */
     public void pokeMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "pokeMenu() hasActivity=" + (mToActivityMessenger != null));
+            Log.d(TAG, "pokeMenu() isMenuVisible=" + isMenuVisible);
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_POKE_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify poke menu", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.pokeMenu();
         }
     }
 
     private void fadeOutMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "fadeOutMenu() state=" + mMenuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
-                    + " callers=\n" + Debug.getCallers(5, "    "));
+            Log.d(TAG, "fadeOutMenu() isMenuVisible=" + isMenuVisible);
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_FADE_OUT_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to fade out", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.fadeOutMenu();
         }
     }
 
@@ -375,19 +232,14 @@
      * Hides the menu activity.
      */
     public void hideMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
             Log.d(TAG, "hideMenu() state=" + mMenuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
+                    + " isMenuVisible=" + isMenuVisible
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to hide", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.hideMenu();
         }
     }
 
@@ -395,29 +247,11 @@
      * Hides the menu activity.
      */
     public void hideMenu(Runnable onStartCallback, Runnable onEndCallback) {
-        if (mStartActivityRequested) {
-            // If the menu has been start-requested, but not actually started, then we defer the
-            // trigger callback until the menu has started and called back to the controller.
-            mOnAnimationEndRunnable = onEndCallback;
-            onStartCallback.run();
-
-            // Fallback for b/63752800, we have started the PipMenuActivity but it has not made any
-            // callbacks. Don't continue to wait for the menu to show past some timeout.
-            mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
-            mHandler.postDelayed(mStartActivityRequestedTimeoutRunnable,
-                    START_ACTIVITY_REQUEST_TIMEOUT_MS);
-        } else if (mMenuState != MENU_STATE_NONE && mToActivityMessenger != null) {
+        if (isMenuVisible()) {
             // If the menu is visible in either the closed or full state, then hide the menu and
             // trigger the animation trigger afterwards
             onStartCallback.run();
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
-            m.obj = onEndCallback;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify hide menu", e);
-            }
+            mPipMenuView.hideMenu(onEndCallback);
         }
     }
 
@@ -438,6 +272,18 @@
         updateMenuActions();
     }
 
+    void onPipExpand() {
+        mListeners.forEach(Listener::onPipExpand);
+    }
+
+    void onPipDismiss() {
+        mListeners.forEach(Listener::onPipDismiss);
+    }
+
+    void onPipShowMenu() {
+        mListeners.forEach(Listener::onPipShowMenu);
+    }
+
     /**
      * @return the best set of actions to show in the PiP menu.
      */
@@ -449,47 +295,20 @@
     }
 
     /**
-     * Starts the menu activity on the top task of the pinned stack.
+     * Returns a default LayoutParams for the PIP Menu.
+     * @param width the PIP stack width.
+     * @param height the PIP stack height.
      */
-    private void startMenuActivity(int menuState, Rect stackBounds, boolean allowMenuTimeout,
-            boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) {
-        try {
-            StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
-                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
-                    pinnedStackInfo.taskIds.length > 0) {
-                Intent intent = new Intent(mContext, mPipMenuActivityClass);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
-                intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
-                if (stackBounds != null) {
-                    intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds);
-                }
-                intent.putExtra(EXTRA_MENU_STATE, menuState);
-                intent.putExtra(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
-                intent.putExtra(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
-                intent.putExtra(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
-                intent.putExtra(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle);
-                ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
-                options.setLaunchTaskId(
-                        pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
-                options.setTaskOverlay(true, true /* canResume */);
-                mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
-                setStartActivityRequested(true);
-            } else {
-                Log.e(TAG, "No PIP tasks found");
-            }
-        } catch (RemoteException e) {
-            setStartActivityRequested(false);
-            Log.e(TAG, "Error showing PIP menu activity", e);
-        }
+    public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) {
+        return new WindowManager.LayoutParams(width, height,
+                WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSLUCENT);
     }
 
     /**
-     * Updates the PiP menu activity with the best set of actions provided.
+     * Updates the PiP menu with the best set of actions provided.
      */
     private void updateMenuActions() {
-        if (mToActivityMessenger != null) {
+        if (isMenuVisible()) {
             // Fetch the pinned stack bounds
             Rect stackBounds = null;
             try {
@@ -499,20 +318,10 @@
                     stackBounds = pinnedStackInfo.bounds;
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Error showing PIP menu activity", e);
+                Log.e(TAG, "Error showing PIP menu", e);
             }
 
-            Bundle data = new Bundle();
-            data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
-            data.putParcelable(EXTRA_ACTIONS, resolveMenuActions());
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS;
-            m.obj = data;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu activity to update actions", e);
-            }
+            mPipMenuView.setActions(stackBounds, resolveMenuActions().getList());
         }
     }
 
@@ -524,17 +333,9 @@
     }
 
     /**
-     * @return whether the time of the activity request has exceeded the timeout.
-     */
-    private boolean isStartActivityRequestedElapsed() {
-        return (SystemClock.uptimeMillis() - mStartActivityRequestedTime)
-                >= START_ACTIVITY_REQUEST_TIMEOUT_MS;
-    }
-
-    /**
      * Handles changes in menu visibility.
      */
-    private void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
+    void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
         if (DEBUG) {
             Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState
                     + " menuState=" + menuState + " resize=" + resize
@@ -556,25 +357,14 @@
         mMenuState = menuState;
     }
 
-    private void setStartActivityRequested(boolean requested) {
-        mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
-        mStartActivityRequested = requested;
-        mStartActivityRequestedTime = requested ? SystemClock.uptimeMillis() : 0;
-    }
-
     /**
      * Handles a pointer event sent from pip input consumer.
      */
     void handlePointerEvent(MotionEvent ev) {
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_POINTER_EVENT;
-            m.obj = ev;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not dispatch touch event", e);
-            }
+        if (ev.isTouchEvent()) {
+            mPipMenuView.dispatchTouchEvent(ev);
+        } else {
+            mPipMenuView.dispatchGenericMotionEvent(ev);
         }
     }
 
@@ -582,15 +372,14 @@
      * Tell the PIP Menu to recalculate its layout given its current position on the display.
      */
     public void updateMenuLayout(Rect bounds) {
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_MENU_LAYOUT;
-            m.obj = bounds;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not dispatch touch event", e);
-            }
+        final boolean isMenuVisible = isMenuVisible();
+        if (DEBUG) {
+            Log.d(TAG, "updateMenuLayout() state=" + mMenuState
+                    + " isMenuVisible=" + isMenuVisible
+                    + " callers=\n" + Debug.getCallers(5, "    "));
+        }
+        if (isMenuVisible) {
+            mPipMenuView.updateMenuLayout(bounds);
         }
     }
 
@@ -598,9 +387,7 @@
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
         pw.println(innerPrefix + "mMenuState=" + mMenuState);
-        pw.println(innerPrefix + "mToActivityMessenger=" + mToActivityMessenger);
+        pw.println(innerPrefix + "mPipMenuView=" + mPipMenuView);
         pw.println(innerPrefix + "mListeners=" + mListeners.size());
-        pw.println(innerPrefix + "mStartActivityRequested=" + mStartActivityRequested);
-        pw.println(innerPrefix + "mStartActivityRequestedTime=" + mStartActivityRequestedTime);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
new file mode 100644
index 0000000..993bfe0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2020 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.pip.phone;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translucent window that gets started on top of a task in PIP to allow the user to control it.
+ */
+public class PipMenuView extends FrameLayout {
+
+    private static final String TAG = "PipMenuView";
+
+    private static final int MESSAGE_INVALID_TYPE = -1;
+    public static final int MESSAGE_MENU_EXPANDED = 8;
+
+    private static final int INITIAL_DISMISS_DELAY = 3500;
+    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
+    private static final long MENU_FADE_DURATION = 125;
+    private static final long MENU_SLOW_FADE_DURATION = 175;
+    private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
+
+    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
+    private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
+
+    private static final float DISABLED_ACTION_ALPHA = 0.54f;
+
+    private static final boolean ENABLE_RESIZE_HANDLE = false;
+
+    private int mMenuState;
+    private boolean mResize = true;
+    private boolean mAllowMenuTimeout = true;
+    private boolean mAllowTouches = true;
+
+    private final List<RemoteAction> mActions = new ArrayList<>();
+
+    private AccessibilityManager mAccessibilityManager;
+    private Drawable mBackgroundDrawable;
+    private View mMenuContainer;
+    private LinearLayout mActionsGroup;
+    private int mBetweenActionPaddingLand;
+
+    private AnimatorSet mMenuContainerAnimator;
+    private PipMenuActivityController mController;
+
+    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    final float alpha = (float) animation.getAnimatedValue();
+                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA * alpha * 255));
+                }
+            };
+
+    private Handler mHandler = new Handler();
+
+    private final Runnable mHideMenuRunnable = this::hideMenu;
+
+    protected View mViewRoot;
+    protected View mSettingsButton;
+    protected View mDismissButton;
+    protected View mResizeHandle;
+    protected View mTopEndContainer;
+    protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
+
+    public PipMenuView(Context context, PipMenuActivityController controller) {
+        super(context, null, 0);
+        mContext = context;
+        mController = controller;
+
+        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+        inflate(context, R.layout.pip_menu, this);
+
+        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
+        mBackgroundDrawable.setAlpha(0);
+        mViewRoot = findViewById(R.id.background);
+        mViewRoot.setBackground(mBackgroundDrawable);
+        mMenuContainer = findViewById(R.id.menu_container);
+        mMenuContainer.setAlpha(0);
+        mTopEndContainer = findViewById(R.id.top_end_container);
+        mSettingsButton = findViewById(R.id.settings);
+        mSettingsButton.setAlpha(0);
+        mSettingsButton.setOnClickListener((v) -> {
+            if (v.getAlpha() != 0) {
+                showSettings();
+            }
+        });
+        mDismissButton = findViewById(R.id.dismiss);
+        mDismissButton.setAlpha(0);
+        mDismissButton.setOnClickListener(v -> dismissPip());
+        findViewById(R.id.expand_button).setOnClickListener(v -> {
+            if (mMenuContainer.getAlpha() != 0) {
+                expandPip();
+            }
+        });
+        // TODO (b/161710689): Remove this once focusability for Windowless window is working
+        findViewById(R.id.expand_button).setFocusable(false);
+        mDismissButton.setFocusable(false);
+        mSettingsButton.setFocusable(false);
+
+        mResizeHandle = findViewById(R.id.resize_handle);
+        mResizeHandle.setAlpha(0);
+        mActionsGroup = findViewById(R.id.actions_group);
+        mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
+                R.dimen.pip_between_action_padding_land);
+        mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
+        mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
+                mResizeHandle, mSettingsButton, mDismissButton);
+
+        initAccessibility();
+    }
+
+    private void initAccessibility() {
+        this.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                String label = getResources().getString(R.string.pip_menu_title);
+                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label));
+            }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
+                    mController.onPipShowMenu();
+                }
+                return super.performAccessibilityAction(host, action, args);
+            }
+        });
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
+            hideMenu();
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (!mAllowTouches) {
+            return false;
+        }
+
+        if (mAllowMenuTimeout) {
+            repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+        }
+
+        return super.dispatchTouchEvent(ev);
+    }
+
+    @Override
+    public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        if (mAllowMenuTimeout) {
+            repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+        }
+
+        return super.dispatchGenericMotionEvent(event);
+    }
+
+    void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+            boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
+        mAllowMenuTimeout = allowMenuTimeout;
+        if (mMenuState != menuState) {
+            // Disallow touches if the menu needs to resize while showing, and we are transitioning
+            // to/from a full menu state.
+            boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow
+                    && (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
+            mAllowTouches = !disallowTouchesUntilAnimationEnd;
+            cancelDelayedHide();
+            updateActionViews(stackBounds);
+            if (mMenuContainerAnimator != null) {
+                mMenuContainerAnimator.cancel();
+            }
+            mMenuContainerAnimator = new AnimatorSet();
+            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 1f);
+            menuAnim.addUpdateListener(mMenuBgUpdateListener);
+            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
+                    mSettingsButton.getAlpha(), 1f);
+            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
+                    mDismissButton.getAlpha(), 1f);
+            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+                    mResizeHandle.getAlpha(),
+                    ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
+                            ? 1f : 0f);
+            if (menuState == MENU_STATE_FULL) {
+                mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+                        resizeAnim);
+            } else {
+                mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
+            }
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
+            mMenuContainerAnimator.setDuration(menuState == MENU_STATE_CLOSE
+                    ? MENU_FADE_DURATION
+                    : MENU_SLOW_FADE_DURATION);
+            if (allowMenuTimeout) {
+                mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        repostDelayedHide(INITIAL_DISMISS_DELAY);
+                    }
+                });
+            }
+            if (withDelay) {
+                // starts the menu container animation after window expansion is completed
+                notifyMenuStateChange(menuState, resizeMenuOnShow, () -> {
+                    if (mMenuContainerAnimator == null) {
+                        return;
+                    }
+                    mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY);
+                    mMenuContainerAnimator.start();
+                });
+            } else {
+                notifyMenuStateChange(menuState, resizeMenuOnShow, null);
+                mMenuContainerAnimator.start();
+            }
+        } else {
+            // If we are already visible, then just start the delayed dismiss and unregister any
+            // existing input consumers from the previous drag
+            if (allowMenuTimeout) {
+                repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+            }
+        }
+    }
+
+    /**
+     * Different from {@link #hideMenu()}, this function does not try to finish this menu activity
+     * and instead, it fades out the controls by setting the alpha to 0 directly without menu
+     * visibility callbacks invoked.
+     */
+    void fadeOutMenu() {
+        mMenuContainer.setAlpha(0f);
+        mSettingsButton.setAlpha(0f);
+        mDismissButton.setAlpha(0f);
+        mResizeHandle.setAlpha(0f);
+    }
+
+    void pokeMenu() {
+        cancelDelayedHide();
+    }
+
+    void onPipAnimationEnded() {
+        mAllowTouches = true;
+    }
+
+    void updateMenuLayout(Rect bounds) {
+        mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
+    }
+
+    void hideMenu() {
+        hideMenu(null);
+    }
+
+    void hideMenu(Runnable animationEndCallback) {
+        hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false);
+    }
+
+    private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
+            boolean animate) {
+        if (mMenuState != MENU_STATE_NONE) {
+            cancelDelayedHide();
+            if (notifyMenuVisibility) {
+                notifyMenuStateChange(MENU_STATE_NONE, mResize, null);
+            }
+            mMenuContainerAnimator = new AnimatorSet();
+            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 0f);
+            menuAnim.addUpdateListener(mMenuBgUpdateListener);
+            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
+                    mSettingsButton.getAlpha(), 0f);
+            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
+                    mDismissButton.getAlpha(), 0f);
+            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+                    mResizeHandle.getAlpha(), 0f);
+            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+            mMenuContainerAnimator.setDuration(animate ? MENU_FADE_DURATION : 0);
+            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (animationFinishedRunnable != null) {
+                        animationFinishedRunnable.run();
+                    }
+                }
+            });
+            mMenuContainerAnimator.start();
+        }
+    }
+
+    void setActions(Rect stackBounds, List<RemoteAction> actions) {
+        mActions.clear();
+        mActions.addAll(actions);
+        updateActionViews(stackBounds);
+    }
+
+    private void updateActionViews(Rect stackBounds) {
+        ViewGroup expandContainer = findViewById(R.id.expand_container);
+        ViewGroup actionsContainer = findViewById(R.id.actions_container);
+        actionsContainer.setOnTouchListener((v, ev) -> {
+            // Do nothing, prevent click through to parent
+            return true;
+        });
+
+        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
+            actionsContainer.setVisibility(View.INVISIBLE);
+        } else {
+            actionsContainer.setVisibility(View.VISIBLE);
+            if (mActionsGroup != null) {
+                // Ensure we have as many buttons as actions
+                final LayoutInflater inflater = LayoutInflater.from(mContext);
+                while (mActionsGroup.getChildCount() < mActions.size()) {
+                    final ImageButton actionView = (ImageButton) inflater.inflate(
+                            R.layout.pip_menu_action, mActionsGroup, false);
+                    mActionsGroup.addView(actionView);
+                }
+
+                // Update the visibility of all views
+                for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
+                    mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
+                            ? View.VISIBLE
+                            : View.GONE);
+                }
+
+                // Recreate the layout
+                final boolean isLandscapePip = stackBounds != null
+                        && (stackBounds.width() > stackBounds.height());
+                for (int i = 0; i < mActions.size(); i++) {
+                    final RemoteAction action = mActions.get(i);
+                    final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
+
+                    // TODO: Check if the action drawable has changed before we reload it
+                    action.getIcon().loadDrawableAsync(mContext, d -> {
+                        d.setTint(Color.WHITE);
+                        actionView.setImageDrawable(d);
+                    }, mHandler);
+                    actionView.setContentDescription(action.getContentDescription());
+                    if (action.isEnabled()) {
+                        actionView.setOnClickListener(v -> {
+                            mHandler.post(() -> {
+                                try {
+                                    action.getActionIntent().send();
+                                } catch (CanceledException e) {
+                                    Log.w(TAG, "Failed to send action", e);
+                                }
+                            });
+                        });
+                    }
+                    actionView.setEnabled(action.isEnabled());
+                    actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+
+                    // Update the margin between actions
+                    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+                            actionView.getLayoutParams();
+                    lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
+                }
+            }
+
+            // Update the expand container margin to adjust the center of the expand button to
+            // account for the existence of the action container
+            FrameLayout.LayoutParams expandedLp =
+                    (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
+            expandedLp.topMargin = getResources().getDimensionPixelSize(
+                    R.dimen.pip_action_padding);
+            expandedLp.bottomMargin = getResources().getDimensionPixelSize(
+                    R.dimen.pip_expand_container_edge_margin);
+            expandContainer.requestLayout();
+        }
+    }
+
+    void updateDismissFraction(float fraction) {
+        int alpha;
+        final float menuAlpha = 1 - fraction;
+        if (mMenuState == MENU_STATE_FULL) {
+            mMenuContainer.setAlpha(menuAlpha);
+            mSettingsButton.setAlpha(menuAlpha);
+            mDismissButton.setAlpha(menuAlpha);
+            final float interpolatedAlpha =
+                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
+            alpha = (int) (interpolatedAlpha * 255);
+        } else {
+            if (mMenuState == MENU_STATE_CLOSE) {
+                mDismissButton.setAlpha(menuAlpha);
+            }
+            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
+        }
+        mBackgroundDrawable.setAlpha(alpha);
+    }
+
+    private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
+        mMenuState = menuState;
+        mController.onMenuStateChanged(menuState, resize, callback);
+    }
+
+    private void expandPip() {
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */);
+    }
+
+    private void dismissPip() {
+        // Since tapping on the close-button invokes a double-tap wait callback in PipTouchHandler,
+        // we want to disable animating the fadeout animation of the buttons in order to call on
+        // PipTouchHandler#onPipDismiss fast enough.
+        final boolean animate = mMenuState != MENU_STATE_CLOSE;
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate);
+    }
+
+    private void showSettings() {
+        final Pair<ComponentName, Integer> topPipActivityInfo =
+                PipUtils.getTopPipActivity(mContext, ActivityManager.getService());
+        if (topPipActivityInfo.first != null) {
+            final UserHandle user = UserHandle.of(topPipActivityInfo.second);
+            final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
+                    Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
+            settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
+            settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+            mContext.startActivity(settingsIntent);
+        }
+    }
+
+    private void cancelDelayedHide() {
+        mHandler.removeCallbacks(mHideMenuRunnable);
+    }
+
+    private void repostDelayedHide(int delay) {
+        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
+        mHandler.removeCallbacks(mHideMenuRunnable);
+        mHandler.postDelayed(mHideMenuRunnable, recommendedTimeout);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 19138fdb..dcee2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -23,6 +23,8 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.view.Choreographer;
 
@@ -66,6 +68,8 @@
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
 
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
     /** PIP's current bounds on the screen. */
     private final Rect mBounds = new Rect();
 
@@ -128,8 +132,10 @@
                         SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
 
     private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
-        mMenuController.updateMenuLayout(newBounds);
-        mBounds.set(newBounds);
+        mMainHandler.post(() -> {
+            mMenuController.updateMenuLayout(newBounds);
+            mBounds.set(newBounds);
+        });
     };
 
     /**
@@ -253,7 +259,9 @@
                 mTemporaryBounds.set(toBounds);
                 mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds,
                         (Rect newBounds) -> {
-                            mMenuController.updateMenuLayout(newBounds);
+                            mMainHandler.post(() -> {
+                                mMenuController.updateMenuLayout(newBounds);
+                            });
                     });
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 2800bb9..08d9b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -15,7 +15,7 @@
  */
 package com.android.systemui.pip.phone;
 
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_RESIZE;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_PINCH_RESIZE;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
@@ -46,6 +46,7 @@
 import android.view.InputEventReceiver;
 import android.view.InputMonitor;
 import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
 import android.view.ViewConfiguration;
 
 import com.android.internal.policy.TaskResizingAlgorithm;
@@ -67,6 +68,8 @@
 public class PipResizeGestureHandler {
 
     private static final String TAG = "PipResizeGestureHandler";
+    private static final float PINCH_THRESHOLD = 0.05f;
+    private static final float STARTING_SCALE_FACTOR = 1.0f;
 
     private static final int INVALID_SYSUI_STATE_MASK =
             SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
@@ -83,6 +86,7 @@
     private final int mDisplayId;
     private final Executor mMainExecutor;
     private final SysUiState mSysUiState;
+    private final ScaleGestureDetector mScaleGestureDetector;
     private final Region mTmpRegion = new Region();
 
     private final PointF mDownPoint = new PointF();
@@ -105,12 +109,15 @@
     private boolean mAllowGesture;
     private boolean mIsAttached;
     private boolean mIsEnabled;
-    private boolean mEnableUserResize;
+    private boolean mEnablePinchResize;
     private boolean mThresholdCrossed;
+    private boolean mUsingPinchToZoom = false;
+    private float mScaleFactor = STARTING_SCALE_FACTOR;
 
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
     private PipTaskOrganizer mPipTaskOrganizer;
+    private PipMenuActivityController mPipMenuActivityController;
     private PipUiEventLogger mPipUiEventLogger;
 
     private int mCtrlType;
@@ -119,7 +126,7 @@
             PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
             PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
             Runnable updateMovementBoundsRunnable, SysUiState sysUiState,
-            PipUiEventLogger pipUiEventLogger) {
+            PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) {
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
@@ -129,22 +136,79 @@
         mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mSysUiState = sysUiState;
+        mPipMenuActivityController = menuActivityController;
         mPipUiEventLogger = pipUiEventLogger;
 
         context.getDisplay().getRealSize(mMaxSize);
         reloadResources();
 
-        mEnableUserResize = DeviceConfig.getBoolean(
+        mScaleGestureDetector = new ScaleGestureDetector(context,
+                new ScaleGestureDetector.OnScaleGestureListener() {
+                    @Override
+                    public boolean onScale(ScaleGestureDetector detector) {
+                        mScaleFactor *= detector.getScaleFactor();
+
+                        if (!mThresholdCrossed
+                                && (mScaleFactor > (STARTING_SCALE_FACTOR + PINCH_THRESHOLD)
+                                || mScaleFactor < (STARTING_SCALE_FACTOR - PINCH_THRESHOLD))) {
+                            mThresholdCrossed = true;
+                            mInputMonitor.pilferPointers();
+                        }
+                        if (mThresholdCrossed) {
+                            int height = Math.min(mMaxSize.y, Math.max(mMinSize.y,
+                                    (int) (mScaleFactor * mLastDownBounds.height())));
+                            int width = Math.min(mMaxSize.x, Math.max(mMinSize.x,
+                                    (int) (mScaleFactor * mLastDownBounds.width())));
+                            int top, bottom, left, right;
+
+                            if ((mCtrlType & CTRL_TOP) != 0) {
+                                top = mLastDownBounds.bottom - height;
+                                bottom = mLastDownBounds.bottom;
+                            } else {
+                                top = mLastDownBounds.top;
+                                bottom = mLastDownBounds.top + height;
+                            }
+
+                            if ((mCtrlType & CTRL_LEFT) != 0) {
+                                left = mLastDownBounds.right - width;
+                                right = mLastDownBounds.right;
+                            } else {
+                                left = mLastDownBounds.left;
+                                right = mLastDownBounds.left + width;
+                            }
+
+                            mLastResizeBounds.set(left, top, right, bottom);
+                            mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds,
+                                    mLastResizeBounds,
+                                    null);
+                        }
+                        return true;
+                    }
+
+                    @Override
+                    public boolean onScaleBegin(ScaleGestureDetector detector) {
+                        setCtrlTypeForPinchToZoom();
+                        return true;
+                    }
+
+                    @Override
+                    public void onScaleEnd(ScaleGestureDetector detector) {
+                        mScaleFactor = STARTING_SCALE_FACTOR;
+                        finishResize();
+                    }
+                });
+
+        mEnablePinchResize = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
-                PIP_USER_RESIZE,
-                /* defaultValue = */ true);
+                PIP_PINCH_RESIZE,
+                /* defaultValue = */ false);
         deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
                 new DeviceConfig.OnPropertiesChangedListener() {
                     @Override
                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                        if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
-                            mEnableUserResize = properties.getBoolean(
-                                    PIP_USER_RESIZE, /* defaultValue = */ true);
+                        if (properties.getKeyset().contains(PIP_PINCH_RESIZE)) {
+                            mEnablePinchResize = properties.getBoolean(
+                                    PIP_PINCH_RESIZE, /* defaultValue = */ false);
                         }
                     }
                 });
@@ -191,7 +255,7 @@
     }
 
     private void updateIsEnabled() {
-        boolean isEnabled = mIsAttached && mEnableUserResize;
+        boolean isEnabled = mIsAttached;
         if (isEnabled == mIsEnabled) {
             return;
         }
@@ -209,7 +273,11 @@
 
     private void onInputEvent(InputEvent ev) {
         if (ev instanceof MotionEvent) {
-            onMotionEvent((MotionEvent) ev);
+            if (mUsingPinchToZoom) {
+                mScaleGestureDetector.onTouchEvent((MotionEvent) ev);
+            } else {
+                onDragCornerResize((MotionEvent) ev);
+            }
         }
     }
 
@@ -252,8 +320,51 @@
     }
 
     public boolean willStartResizeGesture(MotionEvent ev) {
-        return mEnableUserResize && isInValidSysUiState()
-                && isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY());
+        if (isInValidSysUiState()) {
+            switch (ev.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    // Always pass the DOWN event to the ScaleGestureDetector
+                    mScaleGestureDetector.onTouchEvent(ev);
+                    if (isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
+                        return true;
+                    }
+                    break;
+
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    if (mEnablePinchResize && ev.getPointerCount() == 2) {
+                        mUsingPinchToZoom = true;
+                        return true;
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+        return false;
+    }
+
+    private void setCtrlTypeForPinchToZoom() {
+        final Rect currentPipBounds = mMotionHelper.getBounds();
+        mLastDownBounds.set(mMotionHelper.getBounds());
+
+        Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
+        mDisplayBounds.set(movementBounds.left,
+                movementBounds.top,
+                movementBounds.right + currentPipBounds.width(),
+                movementBounds.bottom + currentPipBounds.height());
+
+        if (currentPipBounds.left == mDisplayBounds.left) {
+            mCtrlType |= CTRL_RIGHT;
+        } else {
+            mCtrlType |= CTRL_LEFT;
+        }
+
+        if (currentPipBounds.top > mDisplayBounds.top + mDisplayBounds.height()) {
+            mCtrlType |= CTRL_TOP;
+        } else {
+            mCtrlType |= CTRL_BOTTOM;
+        }
     }
 
     private void setCtrlType(int x, int y) {
@@ -293,11 +404,12 @@
         return (mSysUiState.getFlags() & INVALID_SYSUI_STATE_MASK) == 0;
     }
 
-    private void onMotionEvent(MotionEvent ev) {
+    private void onDragCornerResize(MotionEvent ev) {
         int action = ev.getActionMasked();
         float x = ev.getX();
         float y = ev.getY();
         if (action == MotionEvent.ACTION_DOWN) {
+            final Rect currentPipBounds = mMotionHelper.getBounds();
             mLastResizeBounds.setEmpty();
             mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y);
             if (mAllowGesture) {
@@ -305,6 +417,10 @@
                 mDownPoint.set(x, y);
                 mLastDownBounds.set(mMotionHelper.getBounds());
             }
+            if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
+                    && mPipMenuActivityController.isMenuVisible()) {
+                mPipMenuActivityController.hideMenu();
+            }
 
         } else if (mAllowGesture) {
             switch (action) {
@@ -322,6 +438,10 @@
                         mInputMonitor.pilferPointers();
                     }
                     if (mThresholdCrossed) {
+                        if (mPipMenuActivityController.isMenuVisible()) {
+                            mPipMenuActivityController.hideMenuWithoutResize();
+                            mPipMenuActivityController.hideMenu();
+                        }
                         final Rect currentPipBounds = mMotionHelper.getBounds();
                         mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
                                 mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
@@ -334,28 +454,33 @@
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
-                    if (!mLastResizeBounds.isEmpty()) {
-                        mUserResizeBounds.set(mLastResizeBounds);
-                        mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
-                                (Rect bounds) -> {
-                                    new Handler(Looper.getMainLooper()).post(() -> {
-                                        mMotionHelper.synchronizePinnedStackBounds();
-                                        mUpdateMovementBoundsRunnable.run();
-                                        resetState();
-                                    });
-                                });
-                        mPipUiEventLogger.log(
-                                PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
-                    } else {
-                        resetState();
-                    }
+                    finishResize();
                     break;
             }
         }
     }
 
+    private void finishResize() {
+        if (!mLastResizeBounds.isEmpty()) {
+            mUserResizeBounds.set(mLastResizeBounds);
+            mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
+                    (Rect bounds) -> {
+                        new Handler(Looper.getMainLooper()).post(() -> {
+                            mMotionHelper.synchronizePinnedStackBounds();
+                            mUpdateMovementBoundsRunnable.run();
+                            resetState();
+                        });
+                    });
+            mPipUiEventLogger.log(
+                    PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
+        } else {
+            resetState();
+        }
+    }
+
     private void resetState() {
         mCtrlType = CTRL_NONE;
+        mUsingPinchToZoom = false;
         mAllowGesture = false;
         mThresholdCrossed = false;
     }
@@ -386,7 +511,7 @@
         pw.println(innerPrefix + "mAllowGesture=" + mAllowGesture);
         pw.println(innerPrefix + "mIsAttached=" + mIsAttached);
         pw.println(innerPrefix + "mIsEnabled=" + mIsEnabled);
-        pw.println(innerPrefix + "mEnableUserResize=" + mEnableUserResize);
+        pw.println(innerPrefix + "mEnablePinchResize=" + mEnablePinchResize);
         pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 1b84c14..97b3484 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -230,7 +230,7 @@
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         deviceConfig, pipTaskOrganizer, this::getMovementBounds,
-                        this::updateMovementBounds, sysUiState, pipUiEventLogger);
+                        this::updateMovementBounds, sysUiState, pipUiEventLogger, menuController);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
@@ -650,8 +650,7 @@
         }
 
         MotionEvent ev = (MotionEvent) inputEvent;
-        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
-                && mPipResizeGestureHandler.willStartResizeGesture(ev)) {
+        if (mPipResizeGestureHandler.willStartResizeGesture(ev)) {
             // Initialize the touch state for the gesture, but immediately reset to invalidate the
             // gesture
             mTouchState.onTouchEvent(ev);
@@ -773,10 +772,7 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     private void updateDismissFraction() {
-        // Skip updating the dismiss fraction when the IME is showing. This is to work around an
-        // issue where starting the menu activity for the dismiss overlay will steal the window
-        // focus, which closes the IME.
-        if (mMenuController != null && !mIsImeShowing) {
+        if (mMenuController != null) {
             Rect bounds = mMotionHelper.getBounds();
             final float target = mInsetBounds.bottom;
             float fraction = 0f;
@@ -784,7 +780,7 @@
                 final float distance = bounds.bottom - target;
                 fraction = Math.min(distance / bounds.height(), 1f);
             }
-            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) {
+            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) {
                 // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
                 mMenuController.setDismissFraction(fraction);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
rename to packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
index 8d0948b..3b3235f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
@@ -20,7 +20,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.annotation.NonNull;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityTaskManager;
@@ -51,35 +50,32 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.Pip;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSurfaceTransactionHelper;
 import com.android.systemui.pip.PipTaskOrganizer;
-import com.android.systemui.pip.PipUiEventLogger;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.SplitScreen;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayController;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
-import javax.inject.Inject;
-
 /**
  * Manages the picture-in-picture (PIP) UI and states.
  */
 @SysUISingleton
-public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
-    private static final String TAG = "PipManager";
+public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback {
+    private static final String TAG = "PipController";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
+     * Unknown or invalid state
+     */
+    public static final int STATE_UNKNOWN = -1;
+    /**
      * State when there's no PIP.
      */
     public static final int STATE_NO_PIP = 0;
@@ -142,13 +138,12 @@
     // Used to calculate the movement bounds
     private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
     private final Rect mTmpInsetBounds = new Rect();
-    private final Rect mTmpNormalBounds = new Rect();
 
     // Keeps track of the IME visibility to adjust the PiP when the IME is visible
     private boolean mImeVisible;
     private int mImeHeightAdjustment;
 
-    private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
+    private final PinnedStackListener mPinnedStackListener = new PipControllerPinnedStackListener();
 
     private final Runnable mResizePinnedStackRunnable = new Runnable() {
         @Override
@@ -190,7 +185,7 @@
     /**
      * Handler for messages from the PIP controller.
      */
-    private class PipManagerPinnedStackListener extends PinnedStackListener {
+    private class PipControllerPinnedStackListener extends PinnedStackListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
             mHandler.post(() -> {
@@ -216,10 +211,8 @@
         public void onMovementBoundsChanged(boolean fromImeAdjustment) {
             mHandler.post(() -> {
                 // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
-                final Rect destinationBounds = new Rect();
-                mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
-                        destinationBounds, mTmpDisplayInfo);
-                mDefaultPipBounds.set(destinationBounds);
+                mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBounds,
+                        mDefaultPipBounds, mTmpDisplayInfo);
             });
         }
 
@@ -234,20 +227,19 @@
         }
     }
 
-    @Inject
-    public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
-            ConfigurationController configController,
-            DisplayController displayController,
-            Optional<SplitScreen> splitScreenOptional,
-            @NonNull PipUiEventLogger pipUiEventLogger,
-            ShellTaskOrganizer shellTaskOrganizer) {
+    public PipController(Context context, BroadcastDispatcher broadcastDispatcher,
+            PipBoundsHandler pipBoundsHandler,
+            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+            PipTaskOrganizer pipTaskOrganizer) {
         if (mInitialized) {
             return;
         }
 
         mInitialized = true;
         mContext = context;
-        mPipBoundsHandler = new PipBoundsHandler(mContext);
+        mPipNotification = new PipNotification(context, broadcastDispatcher,
+                Optional.of(this).get());
+        mPipBoundsHandler = pipBoundsHandler;
         // Ensure that we have the display info in case we get calls to update the bounds before the
         // listener calls back
         final DisplayInfo displayInfo = new DisplayInfo();
@@ -256,10 +248,8 @@
 
         mResizeAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
-        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController);
-        mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
-                mPipSurfaceTransactionHelper, splitScreenOptional, displayController,
-                pipUiEventLogger, shellTaskOrganizer);
+        mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
+        mPipTaskOrganizer = pipTaskOrganizer;
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mActivityTaskManager = ActivityTaskManager.getService();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -281,8 +271,6 @@
         } catch (RemoteException | UnsupportedOperationException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
-
-        mPipNotification = new PipNotification(context, broadcastDispatcher, this);
     }
 
     private void loadConfigurationsAndApply(Configuration newConfig) {
@@ -362,7 +350,7 @@
     /**
      * Moves the PIPed activity to the fullscreen and closes PIP system UI.
      */
-    void movePipToFullscreen() {
+    public void movePipToFullscreen() {
         if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription());
 
         mPipTaskId = TASK_ID_NO_PIP;
@@ -375,34 +363,41 @@
 
     /**
      * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called
+     *
      * @param reason The reason for suspending resizing operations on the Pip.
      */
     public void suspendPipResizing(int reason) {
-        if (DEBUG) Log.d(TAG,
-                "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+        if (DEBUG) {
+            Log.d(TAG,
+                    "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+        }
 
         mSuspendPipResizingReason |= reason;
     }
 
     /**
      * Resumes resizing operation on the Pip that was previously suspended.
+     *
      * @param reason The reason resizing operations on the Pip was suspended.
      */
     public void resumePipResizing(int reason) {
         if ((mSuspendPipResizingReason & reason) == 0) {
             return;
         }
-        if (DEBUG) Log.d(TAG,
-                "resumePipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+        if (DEBUG) {
+            Log.d(TAG,
+                    "resumePipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+        }
         mSuspendPipResizingReason &= ~reason;
         mHandler.post(mResizePinnedStackRunnable);
     }
 
     /**
      * Resize the Pip to the appropriate size for the input state.
+     *
      * @param state In Pip state also used to determine the new size for the Pip.
      */
-    void resizePinnedStack(int state) {
+    public void resizePinnedStack(int state) {
         if (DEBUG) {
             Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state="
                     + getStateDescription(), new Exception());
@@ -414,10 +409,12 @@
         }
         if (mSuspendPipResizingReason != 0) {
             mResumeResizePinnedStackRunnableState = state;
-            if (DEBUG) Log.d(TAG, "resizePinnedStack() deferring"
-                    + " mSuspendPipResizingReason=" + mSuspendPipResizingReason
-                    + " mResumeResizePinnedStackRunnableState="
-                    + stateToName(mResumeResizePinnedStackRunnableState));
+            if (DEBUG) {
+                Log.d(TAG, "resizePinnedStack() deferring"
+                        + " mSuspendPipResizingReason=" + mSuspendPipResizingReason
+                        + " mResumeResizePinnedStackRunnableState="
+                        + stateToName(mResumeResizePinnedStackRunnableState));
+            }
             return;
         }
         mState = state;
@@ -474,28 +471,28 @@
     }
 
     /**
-     * Adds a {@link Listener} to PipManager.
+     * Adds a {@link Listener} to PipController.
      */
     public void addListener(Listener listener) {
         mListeners.add(listener);
     }
 
     /**
-     * Removes a {@link Listener} from PipManager.
+     * Removes a {@link Listener} from PipController.
      */
     public void removeListener(Listener listener) {
         mListeners.remove(listener);
     }
 
     /**
-     * Adds a {@link MediaListener} to PipManager.
+     * Adds a {@link MediaListener} to PipController.
      */
     public void addMediaListener(MediaListener listener) {
         mMediaListeners.add(listener);
     }
 
     /**
-     * Removes a {@link MediaListener} from PipManager.
+     * Removes a {@link MediaListener} from PipController.
      */
     public void removeMediaListener(MediaListener listener) {
         mMediaListeners.remove(listener);
@@ -571,16 +568,21 @@
     /**
      * Gets the {@link android.media.session.MediaController} for the PIPed activity.
      */
-    MediaController getMediaController() {
+    public MediaController getMediaController() {
         return mPipMediaController;
     }
 
+    @Override
+    public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
+
+    }
+
     /**
      * Returns the PIPed activity's playback state.
      * This returns one of {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
      * or {@link #PLAYBACK_STATE_UNAVAILABLE}.
      */
-    int getPlaybackState() {
+    public int getPlaybackState() {
         if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
             return PLAYBACK_STATE_UNAVAILABLE;
         }
@@ -676,7 +678,8 @@
     };
 
     @Override
-    public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { }
+    public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
+    }
 
     @Override
     public void onPipTransitionFinished(ComponentName activity, int direction) {
@@ -704,7 +707,7 @@
          * Invoked when an activity is pinned and PIP manager is set corresponding information.
          * Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
          * because there's no guarantee for the PIP manager be return relavent information
-         * correctly. (e.g. {@link isPipShown}).
+         * correctly. (e.g. {@link Pip.isPipShown}).
          */
         void onPipEntered(String packageName);
         /** Invoked when a PIPed activity is closed. */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
index 05bb882..4ecd52f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
@@ -27,12 +27,14 @@
 import android.view.View;
 
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.pip.Pip;
 import com.android.wm.shell.R;
 
 import java.util.ArrayList;
 import java.util.List;
-
-import javax.inject.Inject;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Controller for {@link PipControlsView}.
@@ -43,9 +45,9 @@
     private static final float DISABLED_ACTION_ALPHA = 0.54f;
 
     private final PipControlsView mView;
-    private final PipManager mPipManager;
     private final LayoutInflater mLayoutInflater;
     private final Handler mHandler;
+    private final Optional<Pip> mPipOptional;
     private final PipControlButtonView mPlayPauseButtonView;
     private MediaController mMediaController;
     private PipControlButtonView mFocusedChild;
@@ -73,12 +75,14 @@
                 @Override
                 public void onViewAttachedToWindow(View v) {
                     updateMediaController();
-                    mPipManager.addMediaListener(mPipMediaListener);
+                    mPipOptional.ifPresent(
+                            pip -> pip.addMediaListener(mPipMediaListener));
                 }
 
                 @Override
                 public void onViewDetachedFromWindow(View v) {
-                    mPipManager.removeMediaListener(mPipMediaListener);
+                    mPipOptional.ifPresent(
+                            pip -> pip.removeMediaListener(mPipMediaListener));
                 }
             };
 
@@ -89,7 +93,7 @@
         }
     };
 
-    private final PipManager.MediaListener mPipMediaListener = this::updateMediaController;
+    private final PipController.MediaListener mPipMediaListener = this::updateMediaController;
 
     private final View.OnFocusChangeListener
             mFocusChangeListener =
@@ -105,12 +109,11 @@
             };
 
 
-    @Inject
-    public PipControlsViewController(PipControlsView view, PipManager pipManager,
+    public PipControlsViewController(PipControlsView view, Optional<Pip> pipOptional,
             LayoutInflater layoutInflater, @Main Handler handler) {
         super();
         mView = view;
-        mPipManager = pipManager;
+        mPipOptional = pipOptional;
         mLayoutInflater = layoutInflater;
         mHandler = handler;
 
@@ -121,12 +124,14 @@
 
         View fullButtonView = mView.getFullButtonView();
         fullButtonView.setOnFocusChangeListener(mFocusChangeListener);
-        fullButtonView.setOnClickListener(v -> mPipManager.movePipToFullscreen());
+        fullButtonView.setOnClickListener(
+                v -> mPipOptional.ifPresent(pip -> pip.movePipToFullscreen())
+        );
 
         View closeButtonView = mView.getCloseButtonView();
         closeButtonView.setOnFocusChangeListener(mFocusChangeListener);
         closeButtonView.setOnClickListener(v -> {
-            mPipManager.closePip();
+            mPipOptional.ifPresent(pip -> pip.closePip());
             if (mListener != null) {
                 mListener.onClosed();
             }
@@ -139,24 +144,29 @@
             if (mMediaController == null || mMediaController.getPlaybackState() == null) {
                 return;
             }
-            if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PAUSED) {
-                mMediaController.getTransportControls().play();
-            } else if (mPipManager.getPlaybackState() == PipManager.PLAYBACK_STATE_PLAYING) {
-                mMediaController.getTransportControls().pause();
-            }
+            mPipOptional.ifPresent(pip -> {
+                if (pip.getPlaybackState() == PipController.PLAYBACK_STATE_PAUSED) {
+                    mMediaController.getTransportControls().play();
+                } else if (pip.getPlaybackState() == PipController.PLAYBACK_STATE_PLAYING) {
+                    mMediaController.getTransportControls().pause();
+                }
+            });
+
             // View will be updated later in {@link mMediaControllerCallback}
         });
     }
 
     private void updateMediaController() {
-        MediaController newController = mPipManager.getMediaController();
-        if (mMediaController == newController) {
+        AtomicReference<MediaController> newController = new AtomicReference<>();
+        mPipOptional.ifPresent(pip -> newController.set(pip.getMediaController()));
+
+        if (newController.get() == null || mMediaController == newController.get()) {
             return;
         }
         if (mMediaController != null) {
             mMediaController.unregisterCallback(mMediaControllerCallback);
         }
-        mMediaController = newController;
+        mMediaController = newController.get();
         if (mMediaController != null) {
             mMediaController.registerCallback(mMediaControllerCallback);
         }
@@ -210,12 +220,14 @@
             // Hide the media session buttons
             mPlayPauseButtonView.setVisibility(View.GONE);
         } else {
-            int state = mPipManager.getPlaybackState();
-            if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) {
+            AtomicInteger state = new AtomicInteger(PipController.STATE_UNKNOWN);
+            mPipOptional.ifPresent(pip -> state.set(pip.getPlaybackState()));
+            if (state.get() == PipController.STATE_UNKNOWN
+                    || state.get() == PipController.PLAYBACK_STATE_UNAVAILABLE) {
                 mPlayPauseButtonView.setVisibility(View.GONE);
             } else {
                 mPlayPauseButtonView.setVisibility(View.VISIBLE);
-                if (state == PipManager.PLAYBACK_STATE_PLAYING) {
+                if (state.get() == PipController.PLAYBACK_STATE_PLAYING) {
                     mPlayPauseButtonView.setImageResource(R.drawable.pip_ic_pause_white);
                     mPlayPauseButtonView.setText(R.string.pip_pause);
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 7037403..7e812d9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -25,17 +25,20 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import com.android.systemui.pip.Pip;
 import com.android.systemui.pip.tv.dagger.TvPipComponent;
 import com.android.wm.shell.R;
 
 import java.util.Collections;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
 /**
  * Activity to show the PIP menu to control PIP.
  */
-public class PipMenuActivity extends Activity implements PipManager.Listener {
+
+public class PipMenuActivity extends Activity implements PipController.Listener {
     private static final String TAG = "PipMenuActivity";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -43,7 +46,7 @@
 
     private final TvPipComponent.Builder mPipComponentBuilder;
     private TvPipComponent mTvPipComponent;
-    private final PipManager mPipManager;
+    private final Optional<Pip> mPipOptional;
 
     private Animator mFadeInAnimation;
     private Animator mFadeOutAnimation;
@@ -51,10 +54,11 @@
     private PipControlsViewController mPipControlsViewController;
 
     @Inject
-    public PipMenuActivity(TvPipComponent.Builder pipComponentBuilder, PipManager pipManager) {
+    public PipMenuActivity(TvPipComponent.Builder pipComponentBuilder,
+            Optional<Pip> pipOptional) {
         super();
         mPipComponentBuilder = pipComponentBuilder;
-        mPipManager = pipManager;
+        mPipOptional = pipOptional;
     }
 
     @Override
@@ -62,15 +66,17 @@
         if (DEBUG) Log.d(TAG, "onCreate()");
 
         super.onCreate(bundle);
-        if (!mPipManager.isPipShown()) {
-            finish();
-        }
+        mPipOptional.ifPresent(pip -> {
+            if (!pip.isPipShown()) {
+                finish();
+            }
+        });
         setContentView(R.layout.tv_pip_menu);
         mTvPipComponent = mPipComponentBuilder.pipControlsView(
                 findViewById(R.id.pip_controls)).build();
         mPipControlsViewController = mTvPipComponent.getPipControlsViewController();
 
-        mPipManager.addListener(this);
+        mPipOptional.ifPresent(pip -> pip.addListener(this));
 
         mRestorePipSizeWhenClose = true;
         mFadeInAnimation = AnimatorInflater.loadAnimator(
@@ -98,7 +104,7 @@
             if (DEBUG) Log.d(TAG, "   > restoring to the default position");
 
             // When PIP menu activity is closed, restore to the default position.
-            mPipManager.resizePinnedStack(PipManager.STATE_PIP);
+            mPipOptional.ifPresent(pip -> pip.resizePinnedStack(PipController.STATE_PIP));
         }
         finish();
     }
@@ -125,9 +131,9 @@
         if (DEBUG) Log.d(TAG, "onDestroy()");
 
         super.onDestroy();
-        mPipManager.removeListener(this);
-        mPipManager.resumePipResizing(
-                PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
+        mPipOptional.ifPresent(pip -> pip.removeListener(this));
+        mPipOptional.ifPresent(pip -> pip.resumePipResizing(
+                PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH));
     }
 
     @Override
@@ -178,8 +184,8 @@
         if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()");
 
         finish();
-        mPipManager.suspendPipResizing(
-                PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
+        mPipOptional.ifPresent(pip -> pip.suspendPipResizing(
+                PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 651a4f3..78569ed 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -48,14 +48,14 @@
 public class PipNotification {
     private static final String TAG = "PipNotification";
     private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName();
-    private static final boolean DEBUG = PipManager.DEBUG;
+    private static final boolean DEBUG = PipController.DEBUG;
 
     private static final String ACTION_MENU = "PipNotification.menu";
     private static final String ACTION_CLOSE = "PipNotification.close";
 
     private final PackageManager mPackageManager;
 
-    private final PipManager mPipManager;
+    private final PipController mPipController;
 
     private final NotificationManager mNotificationManager;
     private final Notification.Builder mNotificationBuilder;
@@ -70,7 +70,7 @@
     private String mMediaTitle;
     private Bitmap mArt;
 
-    private PipManager.Listener mPipListener = new PipManager.Listener() {
+    private PipController.Listener mPipListener = new PipController.Listener() {
         @Override
         public void onPipEntered(String packageName) {
             mPackageName = packageName;
@@ -114,22 +114,9 @@
                 notifyPipNotification();
             }
         }
-    };
 
-    private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
         @Override
-        public void onMediaControllerChanged() {
-            MediaController newController = mPipManager.getMediaController();
-            if (mMediaController == newController) {
-                return;
-            }
-            if (mMediaController != null) {
-                mMediaController.unregisterCallback(mMediaControllerCallback);
-            }
-            mMediaController = newController;
-            if (mMediaController != null) {
-                mMediaController.registerCallback(mMediaControllerCallback);
-            }
+        public void onMetadataChanged(MediaMetadata metadata) {
             if (updateMediaControllerMetadata() && mNotified) {
                 // update notification
                 notifyPipNotification();
@@ -137,6 +124,28 @@
         }
     };
 
+    private final PipController.MediaListener mPipMediaListener =
+            new PipController.MediaListener() {
+                @Override
+                public void onMediaControllerChanged() {
+                    MediaController newController = mPipController.getMediaController();
+                    if (newController == null || mMediaController == newController) {
+                        return;
+                    }
+                    if (mMediaController != null) {
+                        mMediaController.unregisterCallback(mMediaControllerCallback);
+                    }
+                    mMediaController = newController;
+                    if (mMediaController != null) {
+                        mMediaController.registerCallback(mMediaControllerCallback);
+                    }
+                    if (updateMediaControllerMetadata() && mNotified) {
+                        // update notification
+                        notifyPipNotification();
+                    }
+                }
+            };
+
     private final BroadcastReceiver mEventReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -145,17 +154,17 @@
             }
             switch (intent.getAction()) {
                 case ACTION_MENU:
-                    mPipManager.showPictureInPictureMenu();
+                    mPipController.showPictureInPictureMenu();
                     break;
                 case ACTION_CLOSE:
-                    mPipManager.closePip();
+                    mPipController.closePip();
                     break;
             }
         }
     };
 
     public PipNotification(Context context, BroadcastDispatcher broadcastDispatcher,
-            PipManager pipManager) {
+            PipController pipController) {
         mPackageManager = context.getPackageManager();
 
         mNotificationManager = (NotificationManager) context.getSystemService(
@@ -169,9 +178,9 @@
                         .setContentIntent(createPendingIntent(context, ACTION_MENU))
                         .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE)));
 
-        mPipManager = pipManager;
-        mPipManager.addListener(mPipListener);
-        mPipManager.addMediaListener(mPipMediaListener);
+        mPipController = pipController;
+        pipController.addListener(mPipListener);
+        pipController.addMediaListener(mPipMediaListener);
 
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(ACTION_MENU);
@@ -182,7 +191,7 @@
     }
 
     /**
-     * Called by {@link PipManager} when the configuration is changed.
+     * Called by {@link PipController} when the configuration is changed.
      */
     void onConfigurationChanged(Context context) {
         Resources res = context.getResources();
@@ -219,8 +228,8 @@
     private boolean updateMediaControllerMetadata() {
         String title = null;
         Bitmap art = null;
-        if (mPipManager.getMediaController() != null) {
-            MediaMetadata metadata = mPipManager.getMediaController().getMetadata();
+        if (mPipController.getMediaController() != null) {
+            MediaMetadata metadata = mPipController.getMediaController().getMetadata();
             if (metadata != null) {
                 title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
                 if (TextUtils.isEmpty(title)) {
@@ -240,6 +249,7 @@
         return false;
     }
 
+
     private String getNotificationTitle() {
         if (!TextUtils.isEmpty(mMediaTitle)) {
             return mMediaTitle;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/PipModule.java b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/PipModule.java
deleted file mode 100644
index 52b38a9..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/PipModule.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019 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.pip.tv.dagger;
-
-import android.app.Activity;
-
-import com.android.systemui.pip.BasePipManager;
-import com.android.systemui.pip.tv.PipManager;
-import com.android.systemui.pip.tv.PipMenuActivity;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
-
-/**
- * Dagger module for TV Pip.
- */
-@Module(subcomponents = {TvPipComponent.class})
-public abstract class PipModule {
-
-    /** Binds PipManager as the default BasePipManager. */
-    @Binds
-    public abstract BasePipManager providePipManager(PipManager pipManager);
-
-
-    /** Inject into PipMenuActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(PipMenuActivity.class)
-    public abstract Activity providePipMenuActivity(PipMenuActivity activity);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 114c30e..8b8941a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.power.dagger;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+import dagger.Binds;
+import dagger.Module;
 
-import javax.inject.Qualifier;
 
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
+/** Dagger Module for code in the power package. */
+@Module
+public interface PowerModule {
+    /** */
+    @Binds
+    PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index e57478e..4d6d71c 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -72,6 +72,8 @@
         private const val ALL_INDICATORS =
                 SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
         private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+        private const val DEFAULT_ALL_INDICATORS = false
+        private const val DEFAULT_MIC_CAMERA = true
     }
 
     @VisibleForTesting
@@ -81,12 +83,12 @@
 
     private fun isAllIndicatorsEnabled(): Boolean {
         return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                ALL_INDICATORS, false)
+                ALL_INDICATORS, DEFAULT_ALL_INDICATORS)
     }
 
     private fun isMicCameraEnabled(): Boolean {
         return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                MIC_CAMERA, false)
+                MIC_CAMERA, DEFAULT_MIC_CAMERA)
     }
 
     private var currentUserIds = emptyList<Int>()
@@ -118,12 +120,13 @@
 
                 // Running on the ui executor so can iterate on callbacks
                 if (properties.keyset.contains(ALL_INDICATORS)) {
-                    allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false)
+                    allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS,
+                            DEFAULT_ALL_INDICATORS)
                     callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
                 }
 
                 if (properties.keyset.contains(MIC_CAMERA)) {
-                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, false)
+                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA)
                     callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
                 }
                 internalUiExecutor.updateListeningState()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 8740581..8ff96c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -22,6 +22,7 @@
 
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.qs.AutoAddTracker;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -29,6 +30,7 @@
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.HotspotController;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 
@@ -56,4 +58,9 @@
         manager.init();
         return manager;
     }
+
+
+    /** */
+    @Binds
+    QSHost provideQsHost(QSTileHost controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index b4f1fe7..5e6a6ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -130,7 +130,8 @@
             return;
         }
         String carrierName = mController.getMobileDataNetworkName();
-        if (TextUtils.isEmpty(carrierName)) {
+        boolean isInService = mController.isMobileDataNetworkInService();
+        if (TextUtils.isEmpty(carrierName) || !isInService) {
             carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
         }
         AlertDialog dialog = new Builder(mContext)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 1a7e229..47002683 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -38,8 +38,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.util.Optional;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index c0cc586..2a976f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
@@ -74,9 +75,8 @@
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.onehanded.OneHandedUI;
+import com.android.systemui.pip.Pip;
 import com.android.systemui.pip.PipAnimationController;
-import com.android.systemui.pip.PipUI;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.shared.recents.IOverviewProxy;
@@ -86,18 +86,21 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
 import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEvents;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import java.util.function.BiConsumer;
 
 import javax.inject.Inject;
 
@@ -121,7 +124,7 @@
     private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
 
     private final Context mContext;
-    private final PipUI mPipUI;
+    private final Optional<Pip> mPipOptional;
     private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
     private final Optional<SplitScreen> mSplitScreenOptional;
     private SysUiState mSysUiState;
@@ -133,7 +136,7 @@
     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
     private final Intent mQuickStepIntent;
     private final ScreenshotHelper mScreenshotHelper;
-    private final OneHandedUI mOneHandedUI;
+    private final Optional<OneHanded> mOneHandedOptional;
     private final CommandQueue mCommandQueue;
 
     private Region mActiveNavBarRegion;
@@ -142,6 +145,7 @@
     private int mConnectionBackoffAttempts;
     private boolean mBound;
     private boolean mIsEnabled;
+    private boolean mHasPipFeature;
     private int mCurrentBoundedUserId = -1;
     private float mNavBarButtonAlpha;
     private boolean mInputFocusTransferStarted;
@@ -382,12 +386,15 @@
 
         @Override
         public void setShelfHeight(boolean visible, int shelfHeight) {
-            if (!verifyCaller("setShelfHeight")) {
+            if (!verifyCaller("setShelfHeight") || !mHasPipFeature) {
+                Log.w(TAG_OPS,
+                        "ByPass setShelfHeight, FEATURE_PICTURE_IN_PICTURE:" + mHasPipFeature);
                 return;
             }
             long token = Binder.clearCallingIdentity();
             try {
-                mPipUI.setShelfHeight(visible, shelfHeight);
+                mPipOptional.ifPresent(
+                        pip -> pip.setShelfHeight(visible, shelfHeight));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -407,12 +414,16 @@
 
         @Override
         public void notifySwipeToHomeFinished() {
-            if (!verifyCaller("notifySwipeToHomeFinished")) {
+            if (!verifyCaller("notifySwipeToHomeFinished") || !mHasPipFeature) {
+                Log.w(TAG_OPS, "ByPass notifySwipeToHomeFinished, FEATURE_PICTURE_IN_PICTURE:"
+                        + mHasPipFeature);
                 return;
             }
             long token = Binder.clearCallingIdentity();
             try {
-                mPipUI.setPinnedStackAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+                mPipOptional.ifPresent(
+                        pip -> pip.setPinnedStackAnimationType(
+                                PipAnimationController.ANIM_TYPE_ALPHA));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -420,12 +431,15 @@
 
         @Override
         public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
-            if (!verifyCaller("setPinnedStackAnimationListener")) {
+            if (!verifyCaller("setPinnedStackAnimationListener") || !mHasPipFeature) {
+                Log.w(TAG_OPS, "ByPass setPinnedStackAnimationListener, FEATURE_PICTURE_IN_PICTURE:"
+                        + mHasPipFeature);
                 return;
             }
             long token = Binder.clearCallingIdentity();
             try {
-                mPipUI.setPinnedStackAnimationListener(listener);
+                mPipOptional.ifPresent(
+                        pip -> pip.setPinnedStackAnimationListener(listener));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -451,9 +465,7 @@
             }
             long token = Binder.clearCallingIdentity();
             try {
-                if (mOneHandedUI != null) {
-                    mOneHandedUI.startOneHanded();
-                }
+                mOneHandedOptional.ifPresent(oneHanded -> oneHanded.startOneHanded());
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -466,9 +478,8 @@
             }
             long token = Binder.clearCallingIdentity();
             try {
-                if (mOneHandedUI != null) {
-                    mOneHandedUI.stopOneHanded();
-                }
+                mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded(
+                                OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT));
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -592,6 +603,8 @@
     };
 
     private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
+    private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener =
+            this::notifySplitScreenBoundsChanged;
 
     // This is the death handler for the binder from the launcher service
     private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
@@ -600,20 +613,23 @@
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
     public OverviewProxyService(Context context, CommandQueue commandQueue,
-            Lazy<NavigationBarController> navBarControllerLazy, NavigationModeController navModeController,
+            Lazy<NavigationBarController> navBarControllerLazy,
+            NavigationModeController navModeController,
             NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
-            PipUI pipUI, Optional<SplitScreen> splitScreenOptional,
-            Optional<Lazy<StatusBar>> statusBarOptionalLazy, OneHandedUI oneHandedUI,
+            Optional<Pip> pipOptional,
+            Optional<SplitScreen> splitScreenOptional,
+            Optional<Lazy<StatusBar>> statusBarOptionalLazy,
+            Optional<OneHanded> oneHandedOptional,
             BroadcastDispatcher broadcastDispatcher) {
         super(broadcastDispatcher);
         mContext = context;
-        mPipUI = pipUI;
+        mPipOptional = pipOptional;
+        mHasPipFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mHandler = new Handler();
         mNavBarControllerLazy = navBarControllerLazy;
         mStatusBarWinController = statusBarWinController;
         mConnectionBackoffAttempts = 0;
-        mSplitScreenOptional = splitScreenOptional;
         mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
                 com.android.internal.R.string.config_recentsComponentName));
         mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
@@ -623,7 +639,7 @@
                 .supportsRoundedCornersOnWindows(mContext.getResources());
         mSysUiState = sysUiState;
         mSysUiState.addCallback(this::notifySystemUiStateFlags);
-        mOneHandedUI = oneHandedUI;
+        mOneHandedOptional = oneHandedOptional;
 
         // Assumes device always starts with back button until launcher tells it that it does not
         mNavBarButtonAlpha = 1.0f;
@@ -653,6 +669,10 @@
         });
         mCommandQueue = commandQueue;
 
+        splitScreenOptional.ifPresent(splitScreen ->
+                splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener));
+        mSplitScreenOptional = splitScreenOptional;
+
         // Listen for user setup
         startTracking();
 
@@ -910,6 +930,7 @@
 
     /**
      * Notifies the Launcher of split screen size changes
+     *
      * @param secondaryWindowBounds Bounds of the secondary window including the insets
      * @param secondaryWindowInsets stable insets received by the secondary window
      */
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 8ec3db5..3bf118d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -40,7 +40,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.LongRunning;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.io.IOException;
@@ -79,12 +79,12 @@
     private final Executor mLongExecutor;
     private final UiEventLogger mUiEventLogger;
     private final NotificationManager mNotificationManager;
-    private final CurrentUserContextTracker mUserContextTracker;
+    private final UserContextProvider mUserContextTracker;
 
     @Inject
     public RecordingService(RecordingController controller, @LongRunning Executor executor,
             UiEventLogger uiEventLogger, NotificationManager notificationManager,
-            CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
+            UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
         mController = controller;
         mLongExecutor = executor;
         mUiEventLogger = uiEventLogger;
@@ -120,7 +120,7 @@
         String action = intent.getAction();
         Log.d(TAG, "onStartCommand " + action);
 
-        int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId();
+        int mCurrentUserId = mUserContextTracker.getUserContext().getUserId();
         UserHandle currentUser = new UserHandle(mCurrentUserId);
         switch (action) {
             case ACTION_START:
@@ -136,7 +136,7 @@
                 setTapsVisible(mShowTaps);
 
                 mRecorder = new ScreenMediaRecorder(
-                        mUserContextTracker.getCurrentUserContext(),
+                        mUserContextTracker.getUserContext(),
                         mCurrentUserId,
                         mAudioSource,
                         this
@@ -156,7 +156,7 @@
                 // we want to post the notifications for that user, which is NOT current user
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                 if (userId == -1) {
-                    userId = mUserContextTracker.getCurrentUserContext().getUserId();
+                    userId = mUserContextTracker.getUserContext().getUserId();
                 }
                 Log.d(TAG, "notifying for user " + userId);
                 stopRecording(userId);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index dc47ab4..2b62a29 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -35,7 +35,7 @@
 import android.widget.Switch;
 
 import com.android.systemui.R;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -51,7 +51,7 @@
     private static final String TAG = "ScreenRecordDialog";
 
     private final RecordingController mController;
-    private final CurrentUserContextTracker mCurrentUserContextTracker;
+    private final UserContextProvider mUserContextProvider;
     private Switch mTapsSwitch;
     private Switch mAudioSwitch;
     private Spinner mOptions;
@@ -59,9 +59,9 @@
 
     @Inject
     public ScreenRecordDialog(RecordingController controller,
-            CurrentUserContextTracker currentUserContextTracker) {
+            UserContextProvider userContextProvider) {
         mController = controller;
-        mCurrentUserContextTracker = currentUserContextTracker;
+        mUserContextProvider = userContextProvider;
     }
 
     @Override
@@ -108,7 +108,7 @@
     }
 
     private void requestScreenCapture() {
-        Context userContext = mCurrentUserContextTracker.getCurrentUserContext();
+        Context userContext = mUserContextProvider.getUserContext();
         boolean showTaps = mTapsSwitch.isChecked();
         ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
                 ? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index e24fbc6..2b4fa2a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -573,7 +573,13 @@
 
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, boolean showFlash) {
-        dismissScreenshot("new screenshot requested", true);
+        if (mScreenshotLayout.isAttachedToWindow()) {
+            // if we didn't already dismiss for another reason
+            if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
+                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
+            }
+            dismissScreenshot("new screenshot requested", true);
+        }
 
         mScreenBitmap = screenshot;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 6b42f2e..74e0229 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -59,7 +59,9 @@
     @UiEvent(doc = "screenshot interaction timed out")
     SCREENSHOT_INTERACTION_TIMEOUT(310),
     @UiEvent(doc = "screenshot explicitly dismissed")
-    SCREENSHOT_EXPLICIT_DISMISSAL(311);
+    SCREENSHOT_EXPLICIT_DISMISSAL(311),
+    @UiEvent(doc = "screenshot reentered for new screenshot")
+    SCREENSHOT_REENTERED(640);
 
     private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
deleted file mode 100644
index d7c4caaa..0000000
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 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.settings
-
-import android.content.ContentResolver
-import android.content.Context
-import android.os.UserHandle
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.util.Assert
-import java.lang.IllegalStateException
-
-/**
- * Tracks a reference to the context for the current user
- *
- * Constructor is injected at SettingsModule
- */
-class CurrentUserContextTracker internal constructor(
-    private val sysuiContext: Context,
-    broadcastDispatcher: BroadcastDispatcher
-) : CurrentUserContentResolverProvider {
-    private val userTracker: CurrentUserTracker
-    private var initialized = false
-
-    private var _curUserContext: Context? = null
-    val currentUserContext: Context
-        get() {
-            if (!initialized) {
-                throw IllegalStateException("Must initialize before getting context")
-            }
-            return _curUserContext!!
-        }
-
-    override val currentUserContentResolver: ContentResolver
-        get() = currentUserContext.contentResolver
-
-    init {
-        userTracker = object : CurrentUserTracker(broadcastDispatcher) {
-            override fun onUserSwitched(newUserId: Int) {
-                handleUserSwitched(newUserId)
-            }
-        }
-    }
-
-    fun initialize() {
-        initialized = true
-        userTracker.startTracking()
-        _curUserContext = makeUserContext(userTracker.currentUserId)
-    }
-
-    @VisibleForTesting
-    fun handleUserSwitched(newUserId: Int) {
-        _curUserContext = makeUserContext(newUserId)
-    }
-
-    private fun makeUserContext(uid: Int): Context {
-        Assert.isMainThread()
-        return sysuiContext.createContextAsUser(UserHandle.of(uid), 0)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
rename to packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
index 9d05843..e0c0c15 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
@@ -18,7 +18,10 @@
 
 import android.content.ContentResolver
 
-interface CurrentUserContentResolverProvider {
+/**
+ * Implemented by [UserTrackerImpl].
+ */
+interface UserContentResolverProvider {
 
-    val currentUserContentResolver: ContentResolver
+    val userContentResolver: ContentResolver
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
similarity index 82%
copy from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
copy to packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
index 9d05843..27af152 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
@@ -16,9 +16,11 @@
 
 package com.android.systemui.settings
 
-import android.content.ContentResolver
+import android.content.Context
 
-interface CurrentUserContentResolverProvider {
-
-    val currentUserContentResolver: ContentResolver
+/**
+ * Implemented by [UserTrackerImpl].
+ */
+interface UserContextProvider {
+    val userContext: Context
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
new file mode 100644
index 0000000..26d408f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.settings
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import java.util.concurrent.Executor
+
+/**
+ * User tracker for SystemUI.
+ *
+ * This tracker provides async access to current user information, as well as callbacks for
+ * user/profile change.
+ */
+interface UserTracker : UserContentResolverProvider, UserContextProvider {
+
+    /**
+     * Current user's id.
+     */
+    val userId: Int
+
+    /**
+     * [UserHandle] for current user
+     */
+    val userHandle: UserHandle
+
+    /**
+     * List of profiles associated with the current user.
+     */
+    val userProfiles: List<UserInfo>
+
+    /**
+     * Add a [Callback] to be notified of chances, on a particular [Executor]
+     */
+    fun addCallback(callback: Callback, executor: Executor)
+
+    /**
+     * Remove a [Callback] previously added.
+     */
+    fun removeCallback(callback: Callback)
+
+    /**
+     * Ćallback for notifying of changes.
+     */
+    interface Callback {
+
+        /**
+         * Notifies that the current user has changed.
+         */
+        @JvmDefault
+        fun onUserChanged(newUser: Int, userContext: Context) {}
+
+        /**
+         * Notifies that the current user's profiles have changed.
+         */
+        @JvmDefault
+        fun onProfilesChanged(profiles: List<UserInfo>) {}
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
new file mode 100644
index 0000000..4cc0eee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2020 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.settings
+
+import android.content.BroadcastReceiver
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import androidx.annotation.GuardedBy
+import androidx.annotation.WorkerThread
+import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.Assert
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.IllegalStateException
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+/**
+ * SystemUI cache for keeping track of the current user and associated values.
+ *
+ * The values provided asynchronously are NOT copies, but shared among all requesters. Do not
+ * modify them.
+ *
+ * This class purposefully doesn't use [BroadcastDispatcher] in order to receive the broadcast as
+ * soon as possible (and reduce its dependency graph).
+ * Other classes that want to listen to the broadcasts listened here SHOULD
+ * subscribe to this class instead.
+ *
+ * @see UserTracker
+ *
+ * Class constructed and initialized in [SettingsModule].
+ */
+class UserTrackerImpl internal constructor(
+    private val context: Context,
+    private val userManager: UserManager,
+    private val dumpManager: DumpManager,
+    private val backgroundHandler: Handler
+) : UserTracker, Dumpable, BroadcastReceiver() {
+
+    companion object {
+        private const val TAG = "UserTrackerImpl"
+    }
+
+    var initialized = false
+        private set
+
+    private val mutex = Any()
+
+    override var userId: Int by SynchronizedDelegate(context.userId)
+        private set
+
+    override var userHandle: UserHandle by SynchronizedDelegate(context.user)
+        private set
+
+    override var userContext: Context by SynchronizedDelegate(context)
+        private set
+
+    override val userContentResolver: ContentResolver
+        get() = userContext.contentResolver
+
+    /**
+     * Returns a [List<UserInfo>] of all profiles associated with the current user.
+     *
+     * The list returned is not a copy, so a copy should be made if its elements need to be
+     * modified.
+     */
+    override var userProfiles: List<UserInfo> by SynchronizedDelegate(emptyList())
+        private set
+
+    @GuardedBy("callbacks")
+    private val callbacks: MutableList<DataItem> = ArrayList()
+
+    fun initialize(startingUser: Int) {
+        if (initialized) {
+            return
+        }
+        initialized = true
+        setUserIdInternal(startingUser)
+
+        val filter = IntentFilter().apply {
+            addAction(Intent.ACTION_USER_SWITCHED)
+            addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+            addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+        }
+        context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+
+        dumpManager.registerDumpable(TAG, this)
+    }
+
+    override fun onReceive(context: Context, intent: Intent) {
+        when (intent.action) {
+            Intent.ACTION_USER_SWITCHED -> {
+                handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
+            }
+            Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE -> {
+                handleProfilesChanged()
+            }
+        }
+    }
+
+    private fun setUserIdInternal(user: Int): Pair<Context, List<UserInfo>> {
+        val profiles = userManager.getProfiles(user)
+        val handle = UserHandle(user)
+        val ctx = context.createContextAsUser(handle, 0)
+
+        synchronized(mutex) {
+            userId = user
+            userHandle = handle
+            userContext = ctx
+            userProfiles = profiles.map { UserInfo(it) }
+        }
+        return ctx to profiles
+    }
+
+    @WorkerThread
+    private fun handleSwitchUser(newUser: Int) {
+        Assert.isNotMainThread()
+        if (newUser == UserHandle.USER_NULL) {
+            Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent")
+            return
+        }
+
+        if (newUser == userId) return
+        Log.i(TAG, "Switching to user $newUser")
+
+        val (ctx, profiles) = setUserIdInternal(newUser)
+
+        notifySubscribers {
+            onUserChanged(newUser, ctx)
+            onProfilesChanged(profiles)
+        }
+    }
+
+    @WorkerThread
+    private fun handleProfilesChanged() {
+        Assert.isNotMainThread()
+
+        val profiles = userManager.getProfiles(userId)
+        synchronized(mutex) {
+            userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy
+        }
+        notifySubscribers {
+            onProfilesChanged(profiles)
+        }
+    }
+
+    override fun addCallback(callback: UserTracker.Callback, executor: Executor) {
+        synchronized(callbacks) {
+            callbacks.add(DataItem(WeakReference(callback), executor))
+        }
+    }
+
+    override fun removeCallback(callback: UserTracker.Callback) {
+        synchronized(callbacks) {
+            callbacks.removeIf { it.sameOrEmpty(callback) }
+        }
+    }
+
+    private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
+        val list = synchronized(callbacks) {
+            callbacks.toList()
+        }
+        list.forEach {
+            if (it.callback.get() != null) {
+                it.executor.execute {
+                    it.callback.get()?.action()
+                }
+            }
+        }
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.println("Initialized: $initialized")
+        if (initialized) {
+            pw.println("userId: $userId")
+            val ids = userProfiles.map { it.id }
+            pw.println("userProfiles: $ids")
+        }
+        val list = synchronized(callbacks) {
+            callbacks.toList()
+        }
+        pw.println("Callbacks:")
+        list.forEach {
+            it.callback.get()?.let {
+                pw.println("  $it")
+            }
+        }
+    }
+
+    private class SynchronizedDelegate<T : Any>(
+        private var value: T
+    ) : ReadWriteProperty<UserTrackerImpl, T> {
+
+        @GuardedBy("mutex")
+        override fun getValue(thisRef: UserTrackerImpl, property: KProperty<*>): T {
+            if (!thisRef.initialized) {
+                throw IllegalStateException("Must initialize before getting ${property.name}")
+            }
+            return synchronized(thisRef.mutex) { value }
+        }
+
+        @GuardedBy("mutex")
+        override fun setValue(thisRef: UserTrackerImpl, property: KProperty<*>, value: T) {
+            synchronized(thisRef.mutex) { this.value = value }
+        }
+    }
+}
+
+private data class DataItem(
+    val callback: WeakReference<UserTracker.Callback>,
+    val executor: Executor
+) {
+    fun sameOrEmpty(other: UserTracker.Callback): Boolean {
+        return callback.get()?.equals(other) ?: true
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
index b1ed772..7084d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
@@ -16,12 +16,18 @@
 
 package com.android.systemui.settings.dagger;
 
+import android.app.ActivityManager;
 import android.content.Context;
+import android.os.Handler;
+import android.os.UserManager;
 
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.settings.CurrentUserContentResolverProvider;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.UserContentResolverProvider;
+import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.settings.UserTrackerImpl;
 
 import dagger.Binds;
 import dagger.Module;
@@ -33,22 +39,27 @@
 @Module
 public abstract class SettingsModule {
 
-    /**
-     * Provides and initializes a CurrentUserContextTracker
-     */
-    @SysUISingleton
-    @Provides
-    static CurrentUserContextTracker provideCurrentUserContextTracker(
-            Context context,
-            BroadcastDispatcher broadcastDispatcher) {
-        CurrentUserContextTracker tracker =
-                new CurrentUserContextTracker(context, broadcastDispatcher);
-        tracker.initialize();
-        return tracker;
-    }
 
     @Binds
     @SysUISingleton
-    abstract CurrentUserContentResolverProvider bindCurrentUserContentResolverTracker(
-            CurrentUserContextTracker tracker);
+    abstract UserContextProvider bindUserContextProvider(UserTracker tracker);
+
+    @Binds
+    @SysUISingleton
+    abstract UserContentResolverProvider bindUserContentResolverProvider(
+            UserTracker tracker);
+
+    @SysUISingleton
+    @Provides
+    static UserTracker provideUserTracker(
+            Context context,
+            UserManager userManager,
+            DumpManager dumpManager,
+            @Background Handler handler
+    ) {
+        int startingUser = ActivityManager.getCurrentUser();
+        UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler);
+        tracker.initialize(startingUser);
+        return tracker;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 0d92d1e..b9b4f42 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -30,8 +30,8 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.stackdivider.DividerView;
-import com.android.systemui.stackdivider.SplitScreen;
+import com.android.wm.shell.splitscreen.DividerView;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.util.Optional;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index eca4c80..0df69a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -185,6 +185,7 @@
         mAlertEntries.put(entry.getKey(), alertEntry);
         onAlertEntryAdded(alertEntry);
         entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.setIsAlerting(true);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 4fa7822..e61e05a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -163,7 +165,7 @@
         if (!mDragDownCallback.isFalsingCheckNeeded()) {
             return false;
         }
-        return mFalsingManager.isFalseTouch() || !mDraggedFarEnough;
+        return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough;
     }
 
     private void captureStartingChild(float x, float y) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 2c29635..4b9d592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -40,6 +40,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 /**
  * An ImageView which does not have overlapping renderings commands and therefore does not need a
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index c01bdc4..8bf134d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
 
 import android.annotation.MainThread;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
@@ -57,6 +58,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
@@ -233,8 +235,17 @@
                     NotificationVisibility visibility,
                     boolean removedByUser,
                     int reason) {
-                onNotificationRemoved(entry.getKey());
-                mediaDataManager.onNotificationRemoved(entry.getKey());
+                removeEntry(entry);
+            }
+        });
+
+        // Pending entries are never inflated, and will never generate a call to onEntryRemoved().
+        // This can happen when notifications are added and canceled before inflation. Add this
+        // separate listener for cleanup, since media inflation occurs onPendingEntryAdded().
+        notificationEntryManager.addCollectionListener(new NotifCollectionListener() {
+            @Override
+            public void onEntryCleanUp(@NonNull NotificationEntry entry) {
+                removeEntry(entry);
             }
         });
 
@@ -247,6 +258,11 @@
                 mPropertiesChangedListener);
     }
 
+    private void removeEntry(NotificationEntry entry) {
+        onNotificationRemoved(entry.getKey());
+        mMediaDataManager.onNotificationRemoved(entry.getKey());
+    }
+
     /**
      * Check if a state should be considered actively playing
      * @param state a PlaybackState
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 8f3033e..7bac007 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -188,7 +188,7 @@
             viewState.openedAmount = openedAmount;
             viewState.clipTopAmount = 0;
             viewState.alpha = 1;
-            viewState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
+            viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
             viewState.hideSensitive = false;
             viewState.xTranslation = getTranslationX();
             if (mNotGoneIndex != -1) {
@@ -352,7 +352,7 @@
         }
         setBackgroundTop(backgroundTop);
         setFirstElementRoundness(firstElementRoundness);
-        mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
+        mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
         mShelfIcons.calculateIconTranslations();
         mShelfIcons.applyIconStates();
         for (int i = 0; i < mHostLayoutController.getChildCount(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 852c055..38c7e5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -36,12 +36,12 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.util.Assert;
 
 import java.util.ArrayList;
@@ -72,7 +72,7 @@
     // Dependencies:
     private final DynamicChildBindController mDynamicChildBindController;
     protected final NotificationLockscreenUserManager mLockscreenUserManager;
-    protected final NotificationGroupManager mGroupManager;
+    protected final NotificationGroupManagerLegacy mGroupManager;
     protected final VisualStabilityManager mVisualStabilityManager;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final NotificationEntryManager mEntryManager;
@@ -107,7 +107,7 @@
             Context context,
             @Main Handler mainHandler,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
             NotificationEntryManager notificationEntryManager,
@@ -187,13 +187,13 @@
             ent.setSensitive(sensitive, deviceSensitive);
             ent.getRow().setNeedsRedaction(needsRedaction);
             mLowPriorityInflationHelper.recheckLowPriorityViewAndInflate(ent, ent.getRow());
-            boolean isChildInGroup = mGroupManager.isChildInGroupWithSummary(ent.getSbn());
+            boolean isChildInGroup = mGroupManager.isChildInGroup(ent);
 
             boolean groupChangesAllowed =
                     mVisualStabilityManager.areGroupChangesAllowed() // user isn't looking at notifs
                     || !ent.hasFinishedInitialization(); // notif recently added
 
-            NotificationEntry parent = mGroupManager.getGroupSummary(ent.getSbn());
+            NotificationEntry parent = mGroupManager.getGroupSummary(ent);
             if (!groupChangesAllowed) {
                 // We don't to change groups while the user is looking at them
                 boolean wasChildInGroup = ent.isChildInGroup();
@@ -431,8 +431,7 @@
         while(!stack.isEmpty()) {
             ExpandableNotificationRow row = stack.pop();
             NotificationEntry entry = row.getEntry();
-            boolean isChildNotification =
-                    mGroupManager.isChildInGroupWithSummary(entry.getSbn());
+            boolean isChildNotification = mGroupManager.isChildInGroup(entry);
 
             if (!onKeyguard) {
                 // If mAlwaysExpandNonGroupedNotification is false, then only expand the
@@ -448,9 +447,8 @@
             boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry);
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
-                if (mGroupManager.isChildInGroupWithSummary(entry.getSbn())) {
-                    NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
-                            entry.getSbn());
+                if (mGroupManager.isChildInGroup(entry)) {
+                    NotificationEntry summary = mGroupManager.getLogicalGroupSummary(entry);
                     if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) {
                         showOnKeyguard = true;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index ba54d1b..6fa3633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.Interpolators
 import com.android.systemui.R
+import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -106,7 +107,7 @@
     private var velocityTracker: VelocityTracker? = null
 
     private val isFalseTouch: Boolean
-        get() = falsingManager.isFalseTouch
+        get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN)
     var qsExpanded: Boolean = false
     var pulseExpandAbortListener: Runnable? = null
     var bouncerShowing: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8cf8a22..01d3103 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -390,17 +390,21 @@
     }
 
     private Drawable getIcon(StatusBarIcon icon) {
-        return getIcon(getContext(), icon);
+        Context notifContext = mNotification != null ?
+                mNotification.getPackageContext(getContext()) : getContext();
+        return getIcon(getContext(), notifContext, icon);
     }
 
     /**
      * Returns the right icon to use for this item
      *
-     * @param context Context to use to get resources
+     * @param sysuiContext Context to use to get scale factor
+     * @param context Context to use to get resources of notification icon
      * @return Drawable for this item, or null if the package or item could not
      *         be found
      */
-    public static Drawable getIcon(Context context, StatusBarIcon statusBarIcon) {
+    public static Drawable getIcon(Context sysuiContext,
+            Context context, StatusBarIcon statusBarIcon) {
         int userId = statusBarIcon.user.getIdentifier();
         if (userId == UserHandle.USER_ALL) {
             userId = UserHandle.USER_SYSTEM;
@@ -409,7 +413,8 @@
         Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
 
         TypedValue typedValue = new TypedValue();
-        context.getResources().getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
+        sysuiContext.getResources().getValue(R.dimen.status_bar_icon_scale_factor,
+                typedValue, true);
         float scaleFactor = typedValue.getFloat();
 
         // No need to scale the icon, so return it as is.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 44550b7..d15b847 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -37,21 +37,29 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.DynamicChildBindController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
@@ -136,6 +144,12 @@
         return new SmartReplyController(entryManager, statusBarService, clickNotifier);
     }
 
+
+    /** */
+    @Binds
+    NotificationRemoteInputManager.Callback provideNotificationRemoteInputManagerCallback(
+            StatusBarRemoteInputCallback callbackImpl);
+
     /** */
     @SysUISingleton
     @Provides
@@ -143,7 +157,7 @@
             Context context,
             @Main Handler mainHandler,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             VisualStabilityManager visualStabilityManager,
             StatusBarStateController statusBarStateController,
             NotificationEntryManager notificationEntryManager,
@@ -179,4 +193,22 @@
     static CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
         return new CommandQueue(context, protoTracer);
     }
+
+    /**
+     */
+    @Binds
+    ManagedProfileController provideManagedProfileController(
+            ManagedProfileControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    SysuiStatusBarStateController providesSysuiStatusBarStateController(
+            StatusBarStateControllerImpl statusBarStateControllerImpl);
+
+    /**
+     */
+    @Binds
+    StatusBarIconController provideStatusBarIconController(
+            StatusBarIconControllerImpl controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index c68625c..433c8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -30,7 +30,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationContentView
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
-import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import java.util.concurrent.ConcurrentHashMap
 import javax.inject.Inject
 
@@ -64,7 +64,7 @@
 @SysUISingleton
 class ConversationNotificationManager @Inject constructor(
     private val notificationEntryManager: NotificationEntryManager,
-    private val notificationGroupManager: NotificationGroupManager,
+    private val notificationGroupManager: NotificationGroupManagerLegacy,
     private val context: Context,
     @Main private val mainHandler: Handler
 ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 838cf0c..6f7b32b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -55,10 +55,10 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.NotificationChannels;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.util.List;
 import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index b5f1c7f..e1e77b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -48,13 +48,13 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.leak.LeakDetector;
 
@@ -139,7 +139,7 @@
     private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
 
     private final KeyguardEnvironment mKeyguardEnvironment;
-    private final NotificationGroupManager mGroupManager;
+    private final NotificationGroupManagerLegacy mGroupManager;
     private final NotificationRankingManager mRankingManager;
     private final FeatureFlags mFeatureFlags;
     private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
@@ -199,7 +199,7 @@
      */
     public NotificationEntryManager(
             NotificationEntryManagerLogger logger,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             NotificationRankingManager rankingManager,
             KeyguardEnvironment keyguardEnvironment,
             FeatureFlags featureFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 590ccf83..73c7fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -34,7 +34,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import javax.inject.Inject;
@@ -46,8 +45,6 @@
 @SysUISingleton
 public class NotificationFilter {
 
-    private final NotificationGroupManager mGroupManager = Dependency.get(
-            NotificationGroupManager.class);
     private final StatusBarStateController mStatusBarStateController;
     private final Boolean mIsMediaFlagEnabled;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 90492b5..fdfd724 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -18,6 +18,7 @@
 
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
@@ -459,8 +460,7 @@
                             + ": has not been marked for removal"));
         }
 
-        if (isDismissedByUser(entry)) {
-            // User-dismissed notifications cannot be lifetime-extended
+        if (cannotBeLifetimeExtended(entry)) {
             cancelLifetimeExtension(entry);
         } else {
             updateLifetimeExtension(entry);
@@ -583,7 +583,7 @@
     }
 
     private void cancelLocalDismissal(NotificationEntry entry) {
-        if (isDismissedByUser(entry)) {
+        if (entry.getDismissState() != NOT_DISMISSED) {
             entry.setDismissState(NOT_DISMISSED);
             if (entry.getSbn().getNotification().isGroupSummary()) {
                 for (NotificationEntry otherEntry : mNotificationSet.values()) {
@@ -669,12 +669,16 @@
      * immediately removed from the collection, but can sometimes stick around due to lifetime
      * extenders.
      */
-    private static boolean isCanceled(NotificationEntry entry) {
+    private boolean isCanceled(NotificationEntry entry) {
         return entry.mCancellationReason != REASON_NOT_CANCELED;
     }
 
-    private static boolean isDismissedByUser(NotificationEntry entry) {
-        return entry.getDismissState() != NOT_DISMISSED;
+    private boolean cannotBeLifetimeExtended(NotificationEntry entry) {
+        final boolean locallyDismissedByUser = entry.getDismissState() != NOT_DISMISSED;
+        final boolean systemServerReportedUserCancel =
+                entry.mCancellationReason == REASON_CLICK
+                        || entry.mCancellationReason == REASON_CANCEL;
+        return locallyDismissedByUser || systemServerReportedUserCancel;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 387247e..789e78e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -60,6 +60,7 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
@@ -69,7 +70,6 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.stack.PriorityBucket;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -177,6 +177,7 @@
     @Nullable private Long mPendingAnimationDuration;
     private boolean mIsMarkedForUserTriggeredMovement;
     private boolean mShelfIconVisible;
+    private boolean mIsAlerting;
 
     /**
      * @param sbn the StatusBarNotification from system server
@@ -429,7 +430,7 @@
      * Get the children that are actually attached to this notification's row.
      *
      * TODO: Seems like most callers here should probably be using
-     * {@link NotificationGroupManager#getChildren}
+     * {@link NotificationGroupManagerLegacy#getChildren}
      */
     public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
         if (row == null) {
@@ -955,6 +956,14 @@
         mIsMarkedForUserTriggeredMovement = marked;
     }
 
+    public void setIsAlerting(boolean isAlerting) {
+        mIsAlerting = isAlerting;
+    }
+
+    public boolean isAlerting() {
+        return mIsAlerting;
+    }
+
     /** Information about a suggestion that is being edited. */
     public static class EditedSuggestionInfo {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index bab2686..fb42c42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.notification.stack.PriorityBucket
-import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import dagger.Lazy
 import java.util.Objects
@@ -52,7 +52,7 @@
  */
 open class NotificationRankingManager @Inject constructor(
     private val mediaManagerLazy: Lazy<NotificationMediaManager>,
-    private val groupManager: NotificationGroupManager,
+    private val groupManager: NotificationGroupManagerLegacy,
     private val headsUpManager: HeadsUpManager,
     private val notifFilter: NotificationFilter,
     private val logger: NotificationEntryManagerLogger,
@@ -191,7 +191,7 @@
     private fun NotificationEntry.isConversation() = getPeopleNotificationType() != TYPE_NON_PERSON
 
     private fun NotificationEntry.getPeopleNotificationType() =
-            peopleNotificationIdentifier.getPeopleNotificationType(sbn, ranking)
+            peopleNotificationIdentifier.getPeopleNotificationType(this)
 
     private fun NotificationEntry.isHighPriority() =
             highPriorityProvider.isHighPriority(this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 2b545c5..df63fec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -673,7 +673,6 @@
                 GroupEntry parent = (GroupEntry) entry;
                 for (NotificationEntry child : parent.getChildren()) {
                     child.getAttachState().setSection(section);
-                    child.getAttachState().setSection(section);
                 }
                 parent.sortChildren(sChildComparator);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index dea1162..f0eb084 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.dagger.PeopleHeader
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import javax.inject.Inject
@@ -33,7 +35,8 @@
  */
 @SysUISingleton
 class ConversationCoordinator @Inject constructor(
-    private val peopleNotificationIdentifier: PeopleNotificationIdentifier
+    private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
+    @PeopleHeader peopleHeaderController: NodeController
 ) : Coordinator {
 
     private val notificationPromoter = object : NotifPromoter(TAG) {
@@ -43,9 +46,9 @@
     }
 
     val sectioner = object : NotifSectioner("People") {
-        override fun isInSection(entry: ListEntry): Boolean {
-            return isConversation(entry.representativeEntry!!)
-        }
+        override fun isInSection(entry: ListEntry): Boolean =
+                isConversation(entry.representativeEntry!!)
+        override fun getHeaderNodeController() = peopleHeaderController
     }
 
     override fun attach(pipeline: NotifPipeline) {
@@ -53,8 +56,7 @@
     }
 
     private fun isConversation(entry: NotificationEntry): Boolean =
-        peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) !=
-            TYPE_NON_PERSON
+        peopleNotificationIdentifier.getPeopleNotificationType(entry) != TYPE_NON_PERSON
 
     companion object {
         private const val TAG = "ConversationCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index c023400..be1383f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -30,6 +30,8 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
+import com.android.systemui.statusbar.notification.dagger.IncomingHeader;
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -61,6 +63,7 @@
     private final HeadsUpViewBinder mHeadsUpViewBinder;
     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final NotificationRemoteInputManager mRemoteInputManager;
+    private final NodeController mIncomingHeaderController;
 
     // tracks the current HeadUpNotification reported by HeadsUpManager
     private @Nullable NotificationEntry mCurrentHun;
@@ -73,11 +76,13 @@
             HeadsUpManager headsUpManager,
             HeadsUpViewBinder headsUpViewBinder,
             NotificationInterruptStateProvider notificationInterruptStateProvider,
-            NotificationRemoteInputManager remoteInputManager) {
+            NotificationRemoteInputManager remoteInputManager,
+            @IncomingHeader NodeController incomingHeaderController) {
         mHeadsUpManager = headsUpManager;
         mHeadsUpViewBinder = headsUpViewBinder;
         mNotificationInterruptStateProvider = notificationInterruptStateProvider;
         mRemoteInputManager = remoteInputManager;
+        mIncomingHeaderController = incomingHeaderController;
     }
 
     @Override
@@ -196,6 +201,12 @@
         public boolean isInSection(ListEntry entry) {
             return isCurrentlyShowingHun(entry);
         }
+
+        @Nullable
+        @Override
+        public NodeController getHeaderNodeController() {
+            return mIncomingHeaderController;
+        }
     };
 
     private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
@@ -204,10 +215,8 @@
         public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
             NotificationEntry newHUN = mHeadsUpManager.getTopEntry();
             if (!Objects.equals(mCurrentHun, newHUN)) {
-                endNotifLifetimeExtension();
                 mCurrentHun = newHUN;
-                mNotifPromoter.invalidateList();
-                mNotifSectioner.invalidateList();
+                endNotifLifetimeExtension();
             }
             if (!isHeadsUp) {
                 mHeadsUpViewBinder.unbindHeadsUpView(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 0f08e0f..133ddfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
+import android.annotation.Nullable;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -24,6 +26,9 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
+import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
+import com.android.systemui.statusbar.notification.dagger.SilentHeader;
 
 import javax.inject.Inject;
 
@@ -40,13 +45,19 @@
 
     private final StatusBarStateController mStatusBarStateController;
     private final HighPriorityProvider mHighPriorityProvider;
+    private final NodeController mSilentHeaderController;
+    private final NodeController mAlertingHeaderController;
 
     @Inject
     public RankingCoordinator(
             StatusBarStateController statusBarStateController,
-            HighPriorityProvider highPriorityProvider) {
+            HighPriorityProvider highPriorityProvider,
+            @AlertingHeader NodeController alertingHeaderController,
+            @SilentHeader NodeController silentHeaderController) {
         mStatusBarStateController = statusBarStateController;
         mHighPriorityProvider = highPriorityProvider;
+        mAlertingHeaderController = alertingHeaderController;
+        mSilentHeaderController = silentHeaderController;
     }
 
     @Override
@@ -70,6 +81,12 @@
         public boolean isInSection(ListEntry entry) {
             return mHighPriorityProvider.isHighPriority(entry);
         }
+
+        @Nullable
+        @Override
+        public NodeController getHeaderNodeController() {
+            return mAlertingHeaderController;
+        }
     };
 
     private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent") {
@@ -77,6 +94,12 @@
         public boolean isInSection(ListEntry entry) {
             return !mHighPriorityProvider.isHighPriority(entry);
         }
+
+        @Nullable
+        @Override
+        public NodeController getHeaderNodeController() {
+            return mSilentHeaderController;
+        }
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
index 6089aa2..aec2647 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
@@ -20,10 +20,10 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import javax.inject.Inject;
 
@@ -34,13 +34,13 @@
 @SysUISingleton
 public class LowPriorityInflationHelper {
     private final FeatureFlags mFeatureFlags;
-    private final NotificationGroupManager mGroupManager;
+    private final NotificationGroupManagerLegacy mGroupManager;
     private final RowContentBindStage mRowContentBindStage;
 
     @Inject
     LowPriorityInflationHelper(
             FeatureFlags featureFlags,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             RowContentBindStage rowContentBindStage) {
         mFeatureFlags = featureFlags;
         mGroupManager = groupManager;
@@ -78,7 +78,7 @@
         if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
             isGroupChild = (entry.getParent() != GroupEntry.ROOT_ENTRY);
         } else {
-            isGroupChild = mGroupManager.isChildInGroupWithSummary(entry.getSbn());
+            isGroupChild = mGroupManager.isChildInGroup(entry);
         }
         return entry.isAmbient() && !isGroupChild;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index c44c59c..21d54c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -11,10 +11,10 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.notification.collection.legacy;
 
 import android.annotation.Nullable;
 import android.service.notification.StatusBarNotification;
@@ -22,14 +22,17 @@
 import android.util.Log;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
@@ -37,6 +40,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -46,13 +50,19 @@
 
 /**
  * A class to handle notifications and their corresponding groups.
+ * This includes:
+ * 1. Determining whether an entry is a member of a group and whether it is a summary or a child
+ * 2. Tracking group expansion states
  */
 @SysUISingleton
-public class NotificationGroupManager implements OnHeadsUpChangedListener, StateListener {
+public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, StateListener,
+        GroupMembershipManager, GroupExpansionManager, Dumpable {
 
     private static final String TAG = "NotificationGroupManager";
     private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
-    private final ArraySet<OnGroupChangeListener> mListeners = new ArraySet<>();
+    private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
+            new ArraySet<>();
+    private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
     private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
     private int mBarState = -1;
     private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
@@ -61,7 +71,7 @@
     @Nullable private BubbleController mBubbleController = null;
 
     @Inject
-    public NotificationGroupManager(
+    public NotificationGroupManagerLegacy(
             StatusBarStateController statusBarStateController,
             Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier) {
         statusBarStateController.addCallback(this);
@@ -77,15 +87,19 @@
 
     /**
      * Add a listener for changes to groups.
-     *
-     * @param listener listener to add
      */
-    public void addOnGroupChangeListener(OnGroupChangeListener listener) {
-        mListeners.add(listener);
+    public void registerGroupChangeListener(OnGroupChangeListener listener) {
+        mGroupChangeListeners.add(listener);
     }
 
-    public boolean isGroupExpanded(StatusBarNotification sbn) {
-        NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
+    @Override
+    public void registerGroupExpansionChangeListener(OnGroupExpansionChangeListener listener) {
+        mExpansionChangeListeners.add(listener);
+    }
+
+    @Override
+    public boolean isGroupExpanded(NotificationEntry entry) {
+        NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
         if (group == null) {
             return false;
         }
@@ -103,8 +117,9 @@
         return group.expanded;
     }
 
-    public void setGroupExpanded(StatusBarNotification sbn, boolean expanded) {
-        NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
+    @Override
+    public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
+        NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
         if (group == null) {
             return;
         }
@@ -114,12 +129,15 @@
     private void setGroupExpanded(NotificationGroup group, boolean expanded) {
         group.expanded = expanded;
         if (group.summary != null) {
-            for (OnGroupChangeListener listener : mListeners) {
-                listener.onGroupExpansionChanged(group.summary.getRow(), expanded);
+            for (OnGroupExpansionChangeListener listener : mExpansionChangeListeners) {
+                listener.onGroupExpansionChange(group.summary.getRow(), expanded);
             }
         }
     }
 
+    /**
+     * When we want to remove an entry from being tracked for grouping
+     */
     public void onEntryRemoved(NotificationEntry removed) {
         onEntryRemovedInternal(removed, removed.getSbn());
         mIsolatedEntries.remove(removed.getKey());
@@ -158,7 +176,7 @@
         if (group.children.isEmpty()) {
             if (group.summary == null) {
                 mGroupMap.remove(groupKey);
-                for (OnGroupChangeListener listener : mListeners) {
+                for (OnGroupChangeListener listener : mGroupChangeListeners) {
                     listener.onGroupRemoved(group, groupKey);
                 }
             }
@@ -184,7 +202,8 @@
         if (group == null) {
             group = new NotificationGroup();
             mGroupMap.put(groupKey, group);
-            for (OnGroupChangeListener listener : mListeners) {
+
+            for (OnGroupChangeListener listener : mGroupChangeListeners) {
                 listener.onGroupCreated(group, groupKey);
             }
         }
@@ -195,9 +214,8 @@
                 Log.wtf(TAG, "Inconsistent entries found with the same key " + added.getKey()
                         + "existing removed: " + existing.isRowRemoved()
                         + (existingThrowable != null
-                                ? Log.getStackTraceString(existingThrowable) + "\n": "")
-                        + " added removed" + added.isRowRemoved()
-                        , new Throwable());
+                                ? Log.getStackTraceString(existingThrowable) + "\n" : "")
+                        + " added removed" + added.isRowRemoved(), new Throwable());
             }
             group.children.put(added.getKey(), added);
             updateSuppression(group);
@@ -206,12 +224,12 @@
             group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
-                ArrayList<NotificationEntry> childrenCopy
-                        = new ArrayList<>(group.children.values());
+                ArrayList<NotificationEntry> childrenCopy =
+                        new ArrayList<>(group.children.values());
                 for (NotificationEntry child : childrenCopy) {
                     onEntryBecomingChild(child);
                 }
-                for (OnGroupChangeListener listener : mListeners) {
+                for (OnGroupChangeListener listener : mGroupChangeListeners) {
                     listener.onGroupCreatedFromChildren(group);
                 }
             }
@@ -243,7 +261,7 @@
                         && group.summary.getSbn().getNotification().isGroupSummary()
                         && (hasIsolatedChildren(group) || hasBubbles)));
         if (prevSuppressed != group.suppressed) {
-            for (OnGroupChangeListener listener : mListeners) {
+            for (OnGroupChangeListener listener : mGroupChangeListeners) {
                 if (!mIsUpdatingUnchangedGroup) {
                     listener.onGroupSuppressionChanged(group, group.suppressed);
                     listener.onGroupsChanged();
@@ -306,6 +324,9 @@
         }
     }
 
+    /**
+     * Whether the given notification is the summary of a group that is being suppressed
+     */
     public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
         return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary();
     }
@@ -315,13 +336,14 @@
                 && getTotalNumberOfChildren(sbn) == 1;
     }
 
-    public boolean isOnlyChildInGroup(StatusBarNotification sbn) {
+    @Override
+    public boolean isOnlyChildInGroup(NotificationEntry entry) {
+        final StatusBarNotification sbn = entry.getSbn();
         if (!isOnlyChild(sbn)) {
             return false;
         }
-        NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn);
-        return logicalGroupSummary != null
-                && !logicalGroupSummary.getSbn().equals(sbn);
+        NotificationEntry logicalGroupSummary = getLogicalGroupSummary(entry);
+        return logicalGroupSummary != null && !logicalGroupSummary.getSbn().equals(sbn);
     }
 
     private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -339,11 +361,12 @@
     private void setStatusBarState(int newState) {
         mBarState = newState;
         if (mBarState == StatusBarState.KEYGUARD) {
-            collapseAllGroups();
+            collapseGroups();
         }
     }
 
-    public void collapseAllGroups() {
+    @Override
+    public void collapseGroups() {
         // Because notifications can become isolated when the group becomes suppressed it can
         // lead to concurrent modifications while looping. We need to make a copy.
         ArrayList<NotificationGroup> groupCopy = new ArrayList<>(mGroupMap.values());
@@ -357,10 +380,9 @@
         }
     }
 
-    /**
-     * @return whether a given notification is a child in a group which has a summary
-     */
-    public boolean isChildInGroupWithSummary(StatusBarNotification sbn) {
+    @Override
+    public boolean isChildInGroup(NotificationEntry entry) {
+        final StatusBarNotification sbn = entry.getSbn();
         if (!isGroupChild(sbn)) {
             return false;
         }
@@ -377,10 +399,9 @@
         return true;
     }
 
-    /**
-     * @return whether a given notification is a summary in a group which has children
-     */
-    public boolean isSummaryOfGroup(StatusBarNotification sbn) {
+    @Override
+    public boolean isGroupSummary(NotificationEntry entry) {
+        final StatusBarNotification sbn = entry.getSbn();
         if (!isGroupSummary(sbn)) {
             return false;
         }
@@ -391,21 +412,14 @@
         return !group.children.isEmpty() && Objects.equals(group.summary.getSbn(), sbn);
     }
 
-    /**
-     * Get the summary of a specified status bar notification. For isolated notification this return
-     * itself.
-     */
-    public NotificationEntry getGroupSummary(StatusBarNotification sbn) {
-        return getGroupSummary(getGroupKey(sbn));
+    @Override
+    public NotificationEntry getGroupSummary(NotificationEntry entry) {
+        return getGroupSummary(getGroupKey(entry.getSbn()));
     }
 
-    /**
-     * Similar to {@link #getGroupSummary(StatusBarNotification)} but doesn't get the visual summary
-     * but the logical summary, i.e when a child is isolated, it still returns the summary as if
-     * it wasn't isolated.
-     */
-    public NotificationEntry getLogicalGroupSummary(StatusBarNotification sbn) {
-        return getGroupSummary(sbn.getGroupKey());
+    @Override
+    public NotificationEntry getLogicalGroupSummary(NotificationEntry entry) {
+        return getGroupSummary(entry.getSbn().getGroupKey());
     }
 
     @Nullable
@@ -436,14 +450,10 @@
         return children;
     }
 
-    /**
-     * Get the children that are in the summary's group, not including those isolated.
-     *
-     * @param summary summary of a group
-     * @return list of the children
-     */
-    public @Nullable ArrayList<NotificationEntry> getChildren(StatusBarNotification summary) {
-        NotificationGroup group = mGroupMap.get(summary.getGroupKey());
+    @Override
+    public @Nullable List<NotificationEntry> getChildren(ListEntry listEntrySummary) {
+        NotificationEntry summary = listEntrySummary.getRepresentativeEntry();
+        NotificationGroup group = mGroupMap.get(summary.getSbn().getGroupKey());
         if (group == null) {
             return null;
         }
@@ -479,9 +489,9 @@
         return groupKey;
     }
 
-    /** @return group expansion state after toggling. */
-    public boolean toggleGroupExpansion(StatusBarNotification sbn) {
-        NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
+    @Override
+    public boolean toggleGroupExpansion(NotificationEntry entry) {
+        NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
         if (group == null) {
             return false;
         }
@@ -494,10 +504,7 @@
     }
 
     /**
-     * Whether a notification is visually a group summary.
-     *
-     * @param sbn notification to check
-     * @return true if it is visually a group summary
+     * Is this notification the summary of a group?
      */
     public boolean isGroupSummary(StatusBarNotification sbn) {
         if (isIsolated(sbn.getKey())) {
@@ -536,14 +543,13 @@
      * @param entry the notification to check
      * @return true if the entry should be isolated
      */
-
     private boolean shouldIsolate(NotificationEntry entry) {
         StatusBarNotification sbn = entry.getSbn();
         if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
             return false;
         }
-        int peopleNotificationType = mPeopleNotificationIdentifier.get().getPeopleNotificationType(
-                entry.getSbn(), entry.getRanking());
+        int peopleNotificationType =
+                mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
         if (peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON) {
             return true;
         }
@@ -576,7 +582,7 @@
         // When the notification gets added afterwards it is already isolated and therefore
         // it doesn't lead to an update.
         updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey()));
-        for (OnGroupChangeListener listener : mListeners) {
+        for (OnGroupChangeListener listener : mGroupChangeListeners) {
             listener.onGroupsChanged();
         }
     }
@@ -607,7 +613,7 @@
             onEntryRemovedInternal(entry, entry.getSbn());
             mIsolatedEntries.remove(sbn.getKey());
             onEntryAddedInternal(entry);
-            for (OnGroupChangeListener listener : mListeners) {
+            for (OnGroupChangeListener listener : mGroupChangeListeners) {
                 listener.onGroupsChanged();
             }
         }
@@ -618,12 +624,16 @@
                 || notificationGroup.summary.isGroupNotFullyVisible();
     }
 
+    /**
+     * Directly set the heads up manager to avoid circular dependencies
+     */
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
     }
 
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("GroupManager state:");
+        pw.println("GroupManagerLegacy state:");
         pw.println("  number of groups: " +  mGroupMap.size());
         for (Map.Entry<String, NotificationGroup>  entry : mGroupMap.entrySet()) {
             pw.println("\n    key: " + entry.getKey()); pw.println(entry.getValue());
@@ -640,6 +650,9 @@
         setStatusBarState(newState);
     }
 
+    /**
+     * Represents a notification group in the notification shade.
+     */
     public static class NotificationGroup {
         public final HashMap<String, NotificationEntry> children = new HashMap<>();
         public NotificationEntry summary;
@@ -659,24 +672,29 @@
             result += "\n    children size: " + children.size();
             for (NotificationEntry child : children.values()) {
                 result += "\n      " + child.getSbn()
-                + (child.getDebugThrowable() != null
-                        ? Log.getStackTraceString(child.getDebugThrowable())
-                        : "");
+                        + (child.getDebugThrowable() != null
+                            ? Log.getStackTraceString(child.getDebugThrowable())
+                            : "");
             }
             result += "\n    summary suppressed: " + suppressed;
             return result;
         }
     }
 
+    /**
+     * Listener for group changes not including group expansion changes which are handled by
+     * {@link OnGroupExpansionChangeListener}.
+     */
     public interface OnGroupChangeListener {
-
         /**
          * A new group has been created.
          *
          * @param group the group that was created
          * @param groupKey the group's key
          */
-        default void onGroupCreated(NotificationGroup group, String groupKey) {}
+        default void onGroupCreated(
+                NotificationGroup group,
+                String groupKey) {}
 
         /**
          * A group has been removed.
@@ -684,7 +702,9 @@
          * @param group the group that was removed
          * @param groupKey the group's key
          */
-        default void onGroupRemoved(NotificationGroup group, String groupKey) {}
+        default void onGroupRemoved(
+                NotificationGroup group,
+                String groupKey) {}
 
         /**
          * The suppression of a group has changed.
@@ -692,16 +712,9 @@
          * @param group the group that has changed
          * @param suppressed true if the group is now suppressed, false o/w
          */
-        default void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {}
-
-        /**
-         * The expansion of a group has changed.
-         *
-         * @param changedRow the row for which the expansion has changed, which is also the summary
-         * @param expanded a boolean indicating the new expanded state
-         */
-        default void onGroupExpansionChanged(ExpandableNotificationRow changedRow,
-                boolean expanded) {}
+        default void onGroupSuppressionChanged(
+                NotificationGroup group,
+                boolean suppressed) {}
 
         /**
          * A group of children just received a summary notification and should therefore become
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
index cce8cdc..610cd33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -38,17 +39,20 @@
     private final HeadsUpManager mHeadsUpManager;
     private final StatusBarStateController mStatusBarStateController;
     private final VisualStabilityManager mVisualStabilityManager;
+    private final GroupMembershipManager mGroupMembershipManager;
 
     public OnUserInteractionCallbackImplLegacy(
             NotificationEntryManager notificationEntryManager,
             HeadsUpManager headsUpManager,
             StatusBarStateController statusBarStateController,
-            VisualStabilityManager visualStabilityManager
+            VisualStabilityManager visualStabilityManager,
+            GroupMembershipManager groupMembershipManager
     ) {
         mNotificationEntryManager = notificationEntryManager;
         mHeadsUpManager = headsUpManager;
         mStatusBarStateController = statusBarStateController;
         mVisualStabilityManager = visualStabilityManager;
+        mGroupMembershipManager = groupMembershipManager;
     }
 
     /**
@@ -69,6 +73,13 @@
             dismissalSurface = NotificationStats.DISMISSAL_AOD;
         }
 
+        if (mGroupMembershipManager.isOnlyChildInGroup(entry)) {
+            NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry);
+            if (groupSummary.isClearable()) {
+                onDismiss(groupSummary, cancellationReason);
+            }
+        }
+
         mNotificationEntryManager.performRemoveNotification(
                 entry.getSbn(),
                 new DismissedByUserStats(
@@ -82,6 +93,7 @@
                                 NotificationLogger.getNotificationLocation(entry))),
                 cancellationReason
         );
+
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index c09122e..c9fc992 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.collection.listbuilder
 
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.NodeController
 
 data class NotifSection(
     val sectioner: NotifSectioner,
@@ -24,4 +25,7 @@
 ) {
     val label: String
         get() = "Section($index, \"${sectioner.name}\")"
+
+    val headerController: NodeController?
+        get() = sectioner.headerNodeController
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
index b57f504..c8982d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
@@ -16,8 +16,12 @@
 
 package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
 
+import android.annotation.Nullable;
+
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
+import com.android.systemui.statusbar.notification.collection.render.NodeSpec;
 
 /**
  * Pluggable for participating in notif sectioning. See {@link ShadeListBuilder#setSections}.
@@ -34,4 +38,12 @@
      * notification. The first section to return true determines the section of the notification.
      */
     public abstract boolean isInSection(ListEntry entry);
+
+    /**
+     * Returns an optional {@link NodeSpec} for the section header. If {@code null}, no header will
+     * be used for the section.
+     */
+    public @Nullable NodeController getHeaderNodeController() {
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
index 05f5ea8..f8fe067 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifLifetimeExtender.java
@@ -53,8 +53,12 @@
      */
     void cancelLifetimeExtension(NotificationEntry entry);
 
-    /** Callback for notifying the NotifCollection that a lifetime extension has expired. */
+    /** Callback for notifying the NotifCollection that a lifetime extension has expired.*/
     interface OnEndLifetimeExtensionCallback {
+        /**
+         * Stop extending the lifetime of `entry` with `extender` and then immediately re-evaluates
+         * whether to continue lifetime extending this notification or to remove it.
+         */
         void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index 8b803b5..18806ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -20,11 +20,10 @@
 import android.app.NotificationManager;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import java.util.List;
 
@@ -39,14 +38,14 @@
 @SysUISingleton
 public class HighPriorityProvider {
     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-    private final NotificationGroupManager mGroupManager;
+    private final GroupMembershipManager mGroupMembershipManager;
 
     @Inject
     public HighPriorityProvider(
             PeopleNotificationIdentifier peopleNotificationIdentifier,
-            NotificationGroupManager groupManager) {
+            GroupMembershipManager groupManager) {
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
-        mGroupManager = groupManager;
+        mGroupMembershipManager = groupManager;
     }
 
     /**
@@ -81,20 +80,15 @@
 
 
     private boolean hasHighPriorityChild(ListEntry entry) {
-        List<NotificationEntry> children = null;
-
-        if (entry instanceof GroupEntry) {
-            // New notification pipeline
-            children = ((GroupEntry) entry).getChildren();
-        } else if (entry.getRepresentativeEntry() != null
-                && mGroupManager.isGroupSummary(entry.getRepresentativeEntry().getSbn())) {
-            // Old notification pipeline
-            children = mGroupManager.getChildren(entry.getRepresentativeEntry().getSbn());
+        if (entry instanceof NotificationEntry
+                && !mGroupMembershipManager.isGroupSummary((NotificationEntry) entry)) {
+            return false;
         }
 
+        List<NotificationEntry> children = mGroupMembershipManager.getChildren(entry);
         if (children != null) {
             for (NotificationEntry child : children) {
-                if (isHighPriority(child)) {
+                if (child != entry && isHighPriority(child)) {
                     return true;
                 }
             }
@@ -122,8 +116,8 @@
     }
 
     private boolean isPeopleNotification(NotificationEntry entry) {
-        return mPeopleNotificationIdentifier.getPeopleNotificationType(
-                entry.getSbn(), entry.getRanking()) != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+        return mPeopleNotificationIdentifier.getPeopleNotificationType(entry)
+                != PeopleNotificationIdentifier.TYPE_NON_PERSON;
     }
 
     private boolean hasUserSetImportance(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManager.java
new file mode 100644
index 0000000..d2df07e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManager.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+/**
+ * Tracks expanded notification states for groups. This expanded state should not be confused by the
+ * expanded/collapsed state of a single notification which is tracked within each
+ * ExpandableNotificationRow.
+ */
+public interface GroupExpansionManager extends Dumpable {
+
+    /**
+     * Register a listener for group expansion changes
+     */
+    void registerGroupExpansionChangeListener(OnGroupExpansionChangeListener listener);
+
+    /**
+     * Whether the group associated with this notification is expanded.
+     * If this notification is not part of a group, it will always return false.
+     */
+    boolean isGroupExpanded(NotificationEntry entry);
+
+    /**
+     * Set whether the group associated with this notification is expanded or not.
+     */
+    void setGroupExpanded(NotificationEntry entry, boolean expanded);
+
+    /** @return group expansion state after toggling. */
+    boolean toggleGroupExpansion(NotificationEntry entry);
+
+    /**
+     * Set expanded=false for all groups
+     */
+    void collapseGroups();
+
+    /**
+     * Listener for group expansion changes.
+     */
+    interface OnGroupExpansionChangeListener {
+        /**
+         * The expansion of a group has changed.
+         *
+         * @param changedRow the row for which the expansion has changed, which is also the summary
+         * @param expanded a boolean indicating the new expanded state
+         */
+        void onGroupExpansionChange(ExpandableNotificationRow changedRow, boolean expanded);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
new file mode 100644
index 0000000..b9aa26f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render;
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.Coordinator;
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Provides grouping information for notification entries including information about a group's
+ * expanded state.
+ */
+public class GroupExpansionManagerImpl implements GroupExpansionManager, Coordinator {
+    private final GroupMembershipManager mGroupMembershipManager;
+    private final Set<OnGroupExpansionChangeListener> mOnGroupChangeListeners = new HashSet<>();
+
+    // Set of summary keys whose groups are expanded
+    private final Set<NotificationEntry> mExpandedGroups = new HashSet<>();
+
+    public GroupExpansionManagerImpl(GroupMembershipManager groupMembershipManager) {
+        mGroupMembershipManager = groupMembershipManager;
+    }
+
+    /**
+     * Cleanup entries from mExpandedGroups that no longer exist in the pipeline.
+     */
+    private final OnBeforeRenderListListener mNotifTracker = (entries) -> {
+        final Set<NotificationEntry> renderingSummaries = new HashSet<>();
+        for (ListEntry entry : entries) {
+            if (entry instanceof GroupEntry) {
+                renderingSummaries.add(entry.getRepresentativeEntry());
+            }
+        }
+        mExpandedGroups.removeIf(expandedGroup -> !renderingSummaries.contains(expandedGroup));
+    };
+
+    @Override
+    public void attach(NotifPipeline pipeline) {
+        pipeline.addOnBeforeRenderListListener(mNotifTracker);
+    }
+
+    @Override
+    public void registerGroupExpansionChangeListener(OnGroupExpansionChangeListener listener) {
+        mOnGroupChangeListeners.add(listener);
+    }
+
+    @Override
+    public boolean isGroupExpanded(NotificationEntry entry) {
+        return mExpandedGroups.contains(mGroupMembershipManager.getGroupSummary(entry));
+    }
+
+    @Override
+    public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
+        final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
+        if (expanded) {
+            mExpandedGroups.add(groupSummary);
+        } else {
+            mExpandedGroups.remove(groupSummary);
+        }
+
+        sendOnGroupExpandedChange(entry, expanded);
+    }
+
+    @Override
+    public boolean toggleGroupExpansion(NotificationEntry entry) {
+        setGroupExpanded(entry, !isGroupExpanded(entry));
+        return isGroupExpanded(entry);
+    }
+
+    @Override
+    public void collapseGroups() {
+        for (NotificationEntry entry : mExpandedGroups) {
+            setGroupExpanded(entry, false);
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("NotificationEntryExpansion state:");
+        pw.println("  # expanded groups: " +  mExpandedGroups.size());
+        for (NotificationEntry entry : mExpandedGroups) {
+            pw.println("    summary key of expanded group: " + entry.getKey());
+        }
+    }
+
+    private void sendOnGroupExpandedChange(NotificationEntry entry, boolean expanded) {
+        for (OnGroupExpansionChangeListener listener : mOnGroupChangeListeners) {
+            listener.onGroupExpansionChange(entry.getRow(), expanded);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
new file mode 100644
index 0000000..196cb8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render;
+
+import android.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.List;
+
+/**
+ * Helper that determines the group states (parent, summary, children) of a notification.
+ */
+public interface GroupMembershipManager {
+    /**
+     * @return whether a given notification is a top level entry or is the summary in a group which
+     * has children
+     */
+    boolean isGroupSummary(NotificationEntry entry);
+
+    /**
+     * Get the summary of a specified status bar notification. For an isolated notification this
+     * returns itself.
+     */
+    NotificationEntry getGroupSummary(NotificationEntry entry);
+
+    /**
+     * Similar to {@link #getGroupSummary(NotificationEntry)} but doesn't get the visual summary
+     * but the logical summary, i.e when a child is isolated, it still returns the summary as if
+     * it wasn't isolated.
+     * TODO: remove this when migrating to the new pipeline, this is taken care of in the
+     * dismissal logic built into NotifCollection
+     */
+    default NotificationEntry getLogicalGroupSummary(NotificationEntry entry) {
+        return getGroupSummary(entry);
+    }
+
+    /**
+     * @return whether a given notification is a child in a group
+     */
+    boolean isChildInGroup(NotificationEntry entry);
+
+    /**
+     * Whether this is the only child in a group
+     * TODO: remove this when migrating to the new pipeline, this is taken care of in the
+     * dismissal logic built into NotifCollection
+     */
+    boolean isOnlyChildInGroup(NotificationEntry entry);
+
+    /**
+     * Get the children that are in the summary's group, not including those isolated.
+     *
+     * @param summary summary of a group
+     * @return list of the children
+     */
+    @Nullable
+    List<NotificationEntry> getChildren(ListEntry summary);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
new file mode 100644
index 0000000..c1f468a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render;
+
+import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.List;
+
+/**
+ * ShadeListBuilder groups notifications from system server. This manager translates
+ * ShadeListBuilder's method of grouping to be used within SystemUI.
+ */
+public class GroupMembershipManagerImpl implements GroupMembershipManager {
+    @Override
+    public boolean isGroupSummary(NotificationEntry entry) {
+        return getGroupSummary(entry) == entry;
+    }
+
+    @Override
+    public NotificationEntry getGroupSummary(NotificationEntry entry) {
+        if (isEntryTopLevel(entry) || entry.getParent() == null) {
+            return null;
+        }
+
+        return entry.getParent().getRepresentativeEntry();
+    }
+
+    @Override
+    public boolean isChildInGroup(NotificationEntry entry) {
+        return !isEntryTopLevel(entry);
+    }
+
+    @Override
+    public boolean isOnlyChildInGroup(NotificationEntry entry) {
+        if (entry.getParent() == null) {
+            return false;
+        }
+
+        return entry.getParent().getChildren().size() == 1;
+    }
+
+    @Nullable
+    @Override
+    public List<NotificationEntry> getChildren(ListEntry entry) {
+        if (entry instanceof GroupEntry) {
+            return ((GroupEntry) entry).getChildren();
+        }
+
+        if (isGroupSummary(entry.getRepresentativeEntry())) {
+            // maybe we were actually passed the summary
+            return entry.getRepresentativeEntry().getParent().getChildren();
+        }
+
+        return null;
+    }
+
+    private boolean isEntryTopLevel(NotificationEntry entry) {
+        return entry.getParent() == ROOT_ENTRY;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
index 67f7b1c..727ce20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
@@ -39,9 +39,7 @@
         throw RuntimeException("Not supported")
     }
 
-    fun getChildCount(): Int {
-        throw RuntimeException("Not supported")
-    }
+    fun getChildCount(): Int = 0
 
     fun addChildAt(child: NodeController, index: Int) {
         throw RuntimeException("Not supported")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
new file mode 100644
index 0000000..498b8e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+import android.annotation.StringRes
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.dagger.HeaderClick
+import com.android.systemui.statusbar.notification.dagger.HeaderText
+import com.android.systemui.statusbar.notification.dagger.NodeLabel
+import com.android.systemui.statusbar.notification.dagger.SectionHeaderScope
+import com.android.systemui.statusbar.notification.stack.SectionHeaderView
+import javax.inject.Inject
+
+interface SectionHeaderController {
+    fun reinflateView(parent: ViewGroup)
+    val headerView: SectionHeaderView?
+    fun setOnClearAllClickListener(listener: View.OnClickListener)
+}
+
+@SectionHeaderScope
+internal class SectionHeaderNodeControllerImpl @Inject constructor(
+    @NodeLabel override val nodeLabel: String,
+    private val layoutInflater: LayoutInflater,
+    @HeaderText @StringRes private val headerTextResId: Int,
+    @HeaderClick private val onHeaderClickListener: View.OnClickListener
+) : NodeController, SectionHeaderController {
+
+    private var _view: SectionHeaderView? = null
+    private var clearAllClickListener: View.OnClickListener? = null
+
+    override fun reinflateView(parent: ViewGroup) {
+        var oldPos = -1
+        _view?.let { _view ->
+            _view.transientContainer?.removeView(_view)
+            if (_view.parent === parent) {
+                oldPos = parent.indexOfChild(_view)
+                parent.removeView(_view)
+            }
+        }
+        val inflated = layoutInflater.inflate(
+                R.layout.status_bar_notification_section_header,
+                parent,
+                false /* attachToRoot */)
+                as SectionHeaderView
+        inflated.setHeaderText(headerTextResId)
+        inflated.setOnHeaderClickListener(onHeaderClickListener)
+        clearAllClickListener?.let { inflated.setOnClearAllClickListener(it) }
+        if (oldPos != -1) {
+            parent.addView(inflated, oldPos)
+        }
+        _view = inflated
+    }
+
+    override val headerView: SectionHeaderView?
+        get() = _view
+
+    override fun setOnClearAllClickListener(listener: View.OnClickListener) {
+        clearAllClickListener = listener
+        _view?.setOnClearAllClickListener(listener)
+    }
+
+    override val view: View
+        get() = _view!!
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 019520f..22ca496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -65,9 +65,7 @@
      *
      * For debugging purposes.
      */
-    fun getViewLabel(view: View): String {
-        return views[view]?.label ?: view.toString()
-    }
+    fun getViewLabel(view: View): String = views[view]?.label ?: view.toString()
 
     private fun detachChildren(
         parentNode: ShadeNode,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 3c35b7bd..44a5c95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import java.lang.RuntimeException
 import javax.inject.Inject
 
 /**
@@ -43,45 +42,35 @@
     private val rootController = RootNodeController(listContainer, View(context))
     private val viewDiffer = ShadeViewDiffer(rootController, logger)
 
-    fun attach(listBuilder: ShadeListBuilder) {
-        listBuilder.setOnRenderListListener(::onNewNotifTree)
-    }
+    fun attach(listBuilder: ShadeListBuilder) =
+            listBuilder.setOnRenderListListener(::onNewNotifTree)
 
-    private fun onNewNotifTree(tree: List<ListEntry>) {
-        viewDiffer.applySpec(buildTree(tree))
-    }
+    private fun onNewNotifTree(tree: List<ListEntry>) = viewDiffer.applySpec(buildTree(tree))
 
     private fun buildTree(notifList: List<ListEntry>): NodeSpec {
-        val root = NodeSpecImpl(null, rootController)
-
-        for (entry in notifList) {
-            // TODO: Add section header logic here
-            root.children.add(buildNotifNode(entry, root))
+        val root = NodeSpecImpl(null, rootController).apply {
+            // Insert first section header, if present
+            notifList.firstOrNull()?.section?.headerController?.let {
+                children.add(NodeSpecImpl(this, it))
+            }
+            notifList.asSequence().zipWithNext().forEach { (prev, entry) ->
+                // Insert new header if the section has changed between two entries
+                entry.section.takeIf { it != prev.section }?.headerController?.let {
+                    children.add(NodeSpecImpl(this, it))
+                }
+                children.add(buildNotifNode(entry, this))
+            }
         }
-
         notificationIconAreaController.updateNotificationIcons(notifList)
         return root
     }
 
     private fun buildNotifNode(entry: ListEntry, parent: NodeSpec): NodeSpec {
         return when (entry) {
-            is NotificationEntry -> {
-                NodeSpecImpl(parent, viewBarn.requireView(entry))
-            }
-            is GroupEntry -> {
-                val groupNode = NodeSpecImpl(
-                        parent,
-                        viewBarn.requireView(checkNotNull(entry.summary)))
-
-                for (childEntry in entry.children) {
-                    groupNode.children.add(buildNotifNode(childEntry, groupNode))
-                }
-
-                groupNode
-            }
-            else -> {
-                throw RuntimeException("Unexpected entry: $entry")
-            }
+            is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireView(entry))
+            is GroupEntry -> NodeSpecImpl(parent, viewBarn.requireView(checkNotNull(entry.summary)))
+                    .apply { entry.children.forEach { children.add(buildNotifNode(it, this)) } }
+            else -> throw RuntimeException("Unexpected entry: $entry")
         }
     }
 }
@@ -92,12 +81,11 @@
     private val viewBarn: NotifViewBarn,
     private val notificationIconAreaController: NotificationIconAreaController
 ) {
-    fun create(listContainer: NotificationListContainer): ShadeViewManager {
-        return ShadeViewManager(
-                context,
-                listContainer,
-                logger,
-                viewBarn,
-                notificationIconAreaController)
-    }
+    fun create(listContainer: NotificationListContainer) =
+            ShadeViewManager(
+                    context,
+                    listContainer,
+                    logger,
+                    viewBarn,
+                    notificationIconAreaController)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
new file mode 100644
index 0000000..179d49c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.dagger
+
+import android.annotation.StringRes
+import android.content.Intent
+import android.provider.Settings
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderNodeControllerImpl
+import dagger.Binds
+import dagger.BindsInstance
+import dagger.Module
+import dagger.Provides
+import dagger.Subcomponent
+import javax.inject.Provider
+import javax.inject.Qualifier
+import javax.inject.Scope
+
+@Module(subcomponents = [SectionHeaderControllerSubcomponent::class])
+object NotificationSectionHeadersModule {
+
+    @Provides
+    @HeaderClick
+    @JvmStatic fun providesOnHeaderClickListener(
+        activityStarter: ActivityStarter
+    ) = View.OnClickListener {
+        activityStarter.startActivity(
+                Intent(Settings.ACTION_NOTIFICATION_SETTINGS),
+                true /* onlyProvisioned */,
+                true /* dismissShade */,
+                Intent.FLAG_ACTIVITY_SINGLE_TOP)
+    }
+
+    @Provides
+    @IncomingHeader
+    @SysUISingleton
+    @JvmStatic fun providesIncomingHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("incoming header")
+            .headerText(R.string.notification_section_header_incoming)
+            .build()
+
+    @Provides
+    @AlertingHeader
+    @SysUISingleton
+    @JvmStatic fun providesAlertingHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("alerting header")
+            .headerText(R.string.notification_section_header_alerting)
+            .build()
+
+    @Provides
+    @PeopleHeader
+    @SysUISingleton
+    @JvmStatic fun providesPeopleHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("people header")
+            .headerText(R.string.notification_section_header_conversations)
+            .build()
+
+    @Provides
+    @SilentHeader
+    @SysUISingleton
+    @JvmStatic fun providesSilentHeaderSubcomponent(
+        builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+    ) = builder.get()
+            .nodeLabel("silent header")
+            .headerText(R.string.notification_section_header_gentle)
+            .build()
+
+    @Provides
+    @SilentHeader
+    @JvmStatic fun providesSilentHeaderNodeController(
+        @SilentHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @SilentHeader
+    @JvmStatic fun providesSilentHeaderController(
+        @SilentHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+
+    @Provides
+    @AlertingHeader
+    @JvmStatic fun providesAlertingHeaderNodeController(
+        @AlertingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @AlertingHeader
+    @JvmStatic fun providesAlertingHeaderController(
+        @AlertingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+
+    @Provides
+    @PeopleHeader
+    @JvmStatic fun providesPeopleHeaderNodeController(
+        @PeopleHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @PeopleHeader
+    @JvmStatic fun providesPeopleHeaderController(
+        @PeopleHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+
+    @Provides
+    @IncomingHeader
+    @JvmStatic fun providesIncomingHeaderNodeController(
+        @IncomingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.nodeController
+
+    @Provides
+    @IncomingHeader
+    @JvmStatic fun providesIncomingHeaderController(
+        @IncomingHeader subcomponent: SectionHeaderControllerSubcomponent
+    ) = subcomponent.headerController
+}
+
+@Subcomponent(modules = [ SectionHeaderBindingModule::class ])
+@SectionHeaderScope
+interface SectionHeaderControllerSubcomponent {
+
+    val nodeController: NodeController
+    val headerController: SectionHeaderController
+
+    @Subcomponent.Builder
+    interface Builder {
+        fun build(): SectionHeaderControllerSubcomponent
+        @BindsInstance fun nodeLabel(@NodeLabel nodeLabel: String): Builder
+        @BindsInstance fun headerText(@HeaderText @StringRes headerText: Int): Builder
+    }
+}
+
+@Module
+private abstract class SectionHeaderBindingModule {
+    @Binds abstract fun bindsNodeController(impl: SectionHeaderNodeControllerImpl): NodeController
+    @Binds abstract fun bindsSectionHeaderController(
+        impl: SectionHeaderNodeControllerImpl
+    ): SectionHeaderController
+}
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class HeaderText
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class IncomingHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class AlertingHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class SilentHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class PeopleHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class NodeLabel
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class HeaderClick
+
+@Scope
+@Retention(AnnotationRetention.BINARY)
+annotation class SectionHeaderScope
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 6d01324..2327063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -34,7 +34,7 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -50,10 +50,15 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -67,7 +72,6 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
@@ -84,14 +88,14 @@
 /**
  * Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
  */
-@Module
+@Module(includes = { NotificationSectionHeadersModule.class })
 public interface NotificationsModule {
     /** Provides an instance of {@link NotificationEntryManager} */
     @SysUISingleton
     @Provides
     static NotificationEntryManager provideNotificationEntryManager(
             NotificationEntryManagerLogger logger,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             NotificationRankingManager rankingManager,
             NotificationEntryManager.KeyguardEnvironment keyguardEnvironment,
             FeatureFlags featureFlags,
@@ -127,7 +131,7 @@
             LauncherApps launcherApps,
             ShortcutManager shortcutManager,
             ChannelEditorDialogController channelEditorDialogController,
-            CurrentUserContextTracker contextTracker,
+            UserContextProvider contextTracker,
             Provider<PriorityOnboardingDialogController.Builder> builderProvider,
             AssistantFeedbackController assistantFeedbackController,
             BubbleController bubbleController,
@@ -201,9 +205,34 @@
             Context context,
             NotificationGutsManager notificationGutsManager,
             NotificationEntryManager notificationEntryManager,
-            MetricsLogger metricsLogger) {
+            MetricsLogger metricsLogger,
+            GroupMembershipManager groupMembershipManager) {
         return new NotificationBlockingHelperManager(
-                context, notificationGutsManager, notificationEntryManager, metricsLogger);
+                context, notificationGutsManager, notificationEntryManager, metricsLogger,
+                groupMembershipManager);
+    }
+
+    /** Provides an instance of {@link GroupMembershipManager} */
+    @SysUISingleton
+    @Provides
+    static GroupMembershipManager provideGroupMembershipManager(
+            FeatureFlags featureFlags,
+            Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) {
+        return featureFlags.isNewNotifPipelineRenderingEnabled()
+                ? new GroupMembershipManagerImpl()
+                : groupManagerLegacy.get();
+    }
+
+    /** Provides an instance of {@link GroupExpansionManager} */
+    @SysUISingleton
+    @Provides
+    static GroupExpansionManager provideGroupExpansionManager(
+            FeatureFlags featureFlags,
+            Lazy<GroupMembershipManager> groupMembershipManager,
+            Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) {
+        return featureFlags.isNewNotifPipelineRenderingEnabled()
+                ? new GroupExpansionManagerImpl(groupMembershipManager.get())
+                : groupManagerLegacy.get();
     }
 
     /** Initializes the notification data pipeline (can be disabled via config). */
@@ -246,7 +275,8 @@
             Lazy<NotifCollection> notifCollection,
             Lazy<VisualStabilityCoordinator> visualStabilityCoordinator,
             NotificationEntryManager entryManager,
-            VisualStabilityManager visualStabilityManager) {
+            VisualStabilityManager visualStabilityManager,
+            Lazy<GroupMembershipManager> groupMembershipManagerLazy) {
         return featureFlags.isNewNotifPipelineRenderingEnabled()
                 ? new OnUserInteractionCallbackImpl(
                         pipeline.get(),
@@ -258,7 +288,8 @@
                         entryManager,
                         headsUpManager,
                         statusBarStateController,
-                        visualStabilityManager);
+                        visualStabilityManager,
+                        groupMembershipManagerLazy.get());
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 6460892..9fb2928 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -30,12 +30,12 @@
 import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
 import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import com.android.systemui.statusbar.notification.interruption.HeadsUpController
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
 import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
-import com.android.systemui.statusbar.phone.NotificationGroupManager
 import com.android.systemui.statusbar.phone.StatusBar
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.HeadsUpManager
@@ -65,7 +65,7 @@
     private val deviceProvisionedController: DeviceProvisionedController,
     private val notificationRowBinder: NotificationRowBinderImpl,
     private val remoteInputUriController: RemoteInputUriController,
-    private val groupManager: NotificationGroupManager,
+    private val groupManagerLegacy: Lazy<NotificationGroupManagerLegacy>,
     private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
     private val headsUpManager: HeadsUpManager,
     private val headsUpController: HeadsUpController,
@@ -111,11 +111,11 @@
         } else {
             targetSdkResolver.initialize(entryManager)
             remoteInputUriController.attach(entryManager)
-            groupAlertTransferHelper.bind(entryManager, groupManager)
-            headsUpManager.addListener(groupManager)
+            groupAlertTransferHelper.bind(entryManager, groupManagerLegacy.get())
+            headsUpManager.addListener(groupManagerLegacy.get())
             headsUpManager.addListener(groupAlertTransferHelper)
             headsUpController.attach(entryManager, headsUpManager)
-            groupManager.setHeadsUpManager(headsUpManager)
+            groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
             groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
 
             entryManager.attach(notificationListener)
@@ -131,7 +131,6 @@
         if (dumpTruck) {
             entryManager.dump(pw, "  ")
         }
-        groupManager.dump(fd, pw, args)
     }
 
     // TODO: Convert all functions below this line into listeners instead of public methods
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index 743bf33..99b2fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -214,7 +214,7 @@
     }
 
     private fun NotificationEntry.extractPerson(): PersonModel? {
-        val type = peopleNotificationIdentifier.getPeopleNotificationType(sbn, ranking)
+        val type = peopleNotificationIdentifier.getPeopleNotificationType(this)
         if (type == TYPE_NON_PERSON) {
             return null
         }
@@ -249,7 +249,7 @@
 
     private fun NotificationEntry.extractPersonKey(): PersonKey? {
         // TODO migrate to shortcut id when snoozing is conversation wide
-        val type = peopleNotificationIdentifier.getPeopleNotificationType(sbn, ranking)
+        val type = peopleNotificationIdentifier.getPeopleNotificationType(this)
         return if (type != TYPE_NON_PERSON) key else null
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 1ac2cb5..0d92616 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -20,12 +20,13 @@
 import android.service.notification.NotificationListenerService.Ranking
 import android.service.notification.StatusBarNotification
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
-import com.android.systemui.statusbar.phone.NotificationGroupManager
 import javax.inject.Inject
 import kotlin.math.max
 
@@ -40,10 +41,12 @@
      *  that users shortcuts.
      */
     @PeopleNotificationType
-    fun getPeopleNotificationType(sbn: StatusBarNotification, ranking: Ranking): Int
+    fun getPeopleNotificationType(entry: NotificationEntry): Int
 
-    fun compareTo(@PeopleNotificationType a: Int,
-                  @PeopleNotificationType b: Int): Int
+    fun compareTo(
+        @PeopleNotificationType a: Int,
+        @PeopleNotificationType b: Int
+    ): Int
 
     companion object {
 
@@ -62,24 +65,27 @@
 @SysUISingleton
 class PeopleNotificationIdentifierImpl @Inject constructor(
     private val personExtractor: NotificationPersonExtractor,
-    private val groupManager: NotificationGroupManager
+    private val groupManager: GroupMembershipManager
 ) : PeopleNotificationIdentifier {
 
     @PeopleNotificationType
-    override fun getPeopleNotificationType(sbn: StatusBarNotification, ranking: Ranking): Int =
-            when (val type = ranking.personTypeInfo) {
+    override fun getPeopleNotificationType(entry: NotificationEntry): Int =
+            when (val type = entry.ranking.personTypeInfo) {
                 TYPE_IMPORTANT_PERSON -> TYPE_IMPORTANT_PERSON
                 else -> {
-                    when (val type = upperBound(type, extractPersonTypeInfo(sbn))) {
+                    when (val type = upperBound(type, extractPersonTypeInfo(entry.sbn))) {
                         TYPE_IMPORTANT_PERSON -> TYPE_IMPORTANT_PERSON
-                        else -> upperBound(type, getPeopleTypeOfSummary(sbn))
+                        else -> upperBound(type, getPeopleTypeOfSummary(entry))
                     }
                 }
             }
 
-    override fun compareTo(@PeopleNotificationType a: Int,
-                  @PeopleNotificationType b: Int): Int {
-        return b.compareTo(a);
+    override fun compareTo(
+        @PeopleNotificationType a: Int,
+        @PeopleNotificationType b: Int
+    ): Int
+    {
+        return b.compareTo(a)
     }
 
     /**
@@ -105,14 +111,14 @@
     private fun extractPersonTypeInfo(sbn: StatusBarNotification) =
             if (personExtractor.isPersonNotification(sbn)) TYPE_PERSON else TYPE_NON_PERSON
 
-    private fun getPeopleTypeOfSummary(statusBarNotification: StatusBarNotification): Int {
-        if (!groupManager.isSummaryOfGroup(statusBarNotification)) {
+    private fun getPeopleTypeOfSummary(entry: NotificationEntry): Int {
+        if (!groupManager.isGroupSummary(entry)) {
             return TYPE_NON_PERSON
         }
 
-        val childTypes = groupManager.getChildren(statusBarNotification)
+        val childTypes = groupManager.getChildren(entry)
                 ?.asSequence()
-                ?.map { getPeopleNotificationType(it.sbn, it.ranking) }
+                ?.map { getPeopleNotificationType(it) }
                 ?: return TYPE_NON_PERSON
 
         var groupType = TYPE_NON_PERSON
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 46b4973..adda049 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -86,6 +86,8 @@
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -97,7 +99,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.SwipeableView;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
@@ -220,7 +221,8 @@
     private boolean mNeedsRedaction;
     private boolean mLastChronometerRunning = true;
     private ViewStub mChildrenContainerStub;
-    private NotificationGroupManager mGroupManager;
+    private GroupMembershipManager mGroupMembershipManager;
+    private GroupExpansionManager mGroupExpansionManager;
     private boolean mChildrenExpanded;
     private boolean mIsSummaryWithChildren;
     private NotificationChildrenContainer mChildrenContainer;
@@ -269,10 +271,10 @@
         @Override
         public void onClick(View v) {
             if (!shouldShowPublic() && (!mIsLowPriority || isExpanded())
-                    && mGroupManager.isSummaryOfGroup(mEntry.getSbn())) {
+                    && mGroupMembershipManager.isGroupSummary(mEntry)) {
                 mGroupExpansionChanging = true;
-                final boolean wasExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn());
-                boolean nowExpanded = mGroupManager.toggleGroupExpansion(mEntry.getSbn());
+                final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
+                boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
                 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
                         nowExpanded);
@@ -527,8 +529,7 @@
     }
 
     private boolean isConversation() {
-        return mPeopleNotificationIdentifier
-                .getPeopleNotificationType(mEntry.getSbn(), mEntry.getRanking())
+        return mPeopleNotificationIdentifier.getPeopleNotificationType(mEntry)
                 != PeopleNotificationIdentifier.TYPE_NON_PERSON;
     }
 
@@ -816,13 +817,6 @@
         return mNotificationParent != null;
     }
 
-    /**
-     * @return whether this notification is the only child in the group summary
-     */
-    public boolean isOnlyChildInGroup() {
-        return mGroupManager.isOnlyChildInGroup(mEntry.getSbn());
-    }
-
     public ExpandableNotificationRow getNotificationParent() {
         return mNotificationParent;
     }
@@ -1424,15 +1418,6 @@
     }
 
     public void performDismiss(boolean fromAccessibility) {
-        if (isOnlyChildInGroup()) {
-            NotificationEntry groupSummary =
-                    mGroupManager.getLogicalGroupSummary(mEntry.getSbn());
-            if (groupSummary.isClearable()) {
-                // If this is the only child in the group, dismiss the group, but don't try to show
-                // the blocking helper affordance!
-                groupSummary.getRow().performDismiss(fromAccessibility);
-            }
-        }
         dismiss(fromAccessibility);
         if (mEntry.isClearable()) {
             if (mOnUserInteractionCallback != null) {
@@ -1579,7 +1564,8 @@
             String notificationKey,
             ExpansionLogger logger,
             KeyguardBypassController bypassController,
-            NotificationGroupManager groupManager,
+            GroupMembershipManager groupMembershipManager,
+            GroupExpansionManager groupExpansionManager,
             HeadsUpManager headsUpManager,
             RowContentBindStage rowContentBindStage,
             OnExpandClickListener onExpandClickListener,
@@ -1600,8 +1586,9 @@
         mLogger = logger;
         mLoggingKey = notificationKey;
         mBypassController = bypassController;
-        mGroupManager = groupManager;
-        mPrivateLayout.setGroupManager(groupManager);
+        mGroupMembershipManager = groupMembershipManager;
+        mGroupExpansionManager = groupExpansionManager;
+        mPrivateLayout.setGroupMembershipManager(groupMembershipManager);
         mHeadsUpManager = headsUpManager;
         mRowContentBindStage = rowContentBindStage;
         mOnExpandClickListener = onExpandClickListener;
@@ -2184,8 +2171,8 @@
         mFalsingManager.setNotificationExpanded();
         if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
                 && !mChildrenContainer.showingAsLowPriority()) {
-            final boolean wasExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn());
-            mGroupManager.setGroupExpanded(mEntry.getSbn(), userExpanded);
+            final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
+            mGroupExpansionManager.setGroupExpanded(mEntry, userExpanded);
             onExpansionChanged(true /* userAction */, wasExpanded);
             return;
         }
@@ -2328,7 +2315,7 @@
 
     @Override
     public boolean isGroupExpanded() {
-        return mGroupManager.isGroupExpanded(mEntry.getSbn());
+        return mGroupExpansionManager.isGroupExpanded(mEntry);
     }
 
     private void onAttachedChildrenCountChanged() {
@@ -2574,7 +2561,7 @@
     public void makeActionsVisibile() {
         setUserExpanded(true, true);
         if (isChildInGroup()) {
-            mGroupManager.setGroupExpanded(mEntry.getSbn(), true);
+            mGroupExpansionManager.setGroupExpanded(mEntry, true);
         }
         notifyHeightChanged(false /* needsAnimation */);
     }
@@ -2867,7 +2854,7 @@
 
     public void onExpandedByGesture(boolean userExpanded) {
         int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
-        if (mGroupManager.isSummaryOfGroup(mEntry.getSbn())) {
+        if (mGroupMembershipManager.isGroupSummary(mEntry)) {
             event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
         }
         MetricsLogger.action(mContext, event, userExpanded);
@@ -2912,7 +2899,7 @@
     private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
         boolean nowExpanded = isExpanded();
         if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
-            nowExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn());
+            nowExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
         }
         if (nowExpanded != wasExpanded) {
             updateShelfIconColor();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index ce760cb..1d72557 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -31,6 +31,8 @@
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -39,7 +41,6 @@
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.time.SystemClock;
 
@@ -62,7 +63,8 @@
     private final String mAppName;
     private final String mNotificationKey;
     private final KeyguardBypassController mKeyguardBypassController;
-    private final NotificationGroupManager mNotificationGroupManager;
+    private final GroupMembershipManager mGroupMembershipManager;
+    private final GroupExpansionManager mGroupExpansionManager;
     private final RowContentBindStage mRowContentBindStage;
     private final NotificationLogger mNotificationLogger;
     private final HeadsUpManager mHeadsUpManager;
@@ -85,7 +87,8 @@
             NotificationMediaManager mediaManager, PluginManager pluginManager,
             SystemClock clock, @AppName String appName, @NotificationKey String notificationKey,
             KeyguardBypassController keyguardBypassController,
-            NotificationGroupManager notificationGroupManager,
+            GroupMembershipManager groupMembershipManager,
+            GroupExpansionManager groupExpansionManager,
             RowContentBindStage rowContentBindStage,
             NotificationLogger notificationLogger, HeadsUpManager headsUpManager,
             ExpandableNotificationRow.OnExpandClickListener onExpandClickListener,
@@ -103,7 +106,8 @@
         mAppName = appName;
         mNotificationKey = notificationKey;
         mKeyguardBypassController = keyguardBypassController;
-        mNotificationGroupManager = notificationGroupManager;
+        mGroupMembershipManager = groupMembershipManager;
+        mGroupExpansionManager = groupExpansionManager;
         mRowContentBindStage = rowContentBindStage;
         mNotificationLogger = notificationLogger;
         mHeadsUpManager = headsUpManager;
@@ -128,7 +132,8 @@
                 mNotificationKey,
                 mExpansionLogger,
                 mKeyguardBypassController,
-                mNotificationGroupManager,
+                mGroupMembershipManager,
+                mGroupExpansionManager,
                 mHeadsUpManager,
                 mRowContentBindStage,
                 mOnExpandClickListener,
@@ -140,13 +145,6 @@
                 mOnUserInteractionCallback
 
         );
-        mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
-            @Override
-            public void onStateChanged(int newState) {
-                mView.setOnKeyguard(newState == KEYGUARD);
-            }
-        });
-        mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (mAllowLongPress) {
             mView.setLongPressListener((v, x, y, item) -> {
@@ -167,15 +165,26 @@
                 mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
                 mPluginManager.addPluginListener(mView,
                         NotificationMenuRowPlugin.class, false /* Allow multiple */);
+                mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
+                mStatusBarStateController.addCallback(mStatusBarStateListener);
             }
 
             @Override
             public void onViewDetachedFromWindow(View v) {
                 mPluginManager.removePluginListener(mView);
+                mStatusBarStateController.removeCallback(mStatusBarStateListener);
             }
         });
     }
 
+    private final StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    mView.setOnKeyguard(newState == KEYGUARD);
+                }
+            };
+
     private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
         mNotificationLogger.onExpansionChanged(key, userAction, expanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 9212325..ab78d19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -28,6 +28,8 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 
@@ -48,6 +50,7 @@
     private final NotificationGutsManager mNotificationGutsManager;
     private final NotificationEntryManager mNotificationEntryManager;
     private final MetricsLogger mMetricsLogger;
+    private final GroupMembershipManager mGroupMembershipManager;
     /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
     private ExpandableNotificationRow mBlockingHelperRow;
     private Set<String> mNonBlockablePkgs;
@@ -65,7 +68,8 @@
             Context context,
             NotificationGutsManager notificationGutsManager,
             NotificationEntryManager notificationEntryManager,
-            MetricsLogger metricsLogger) {
+            MetricsLogger metricsLogger,
+            GroupMembershipManager groupMembershipManager) {
         mContext = context;
         mNotificationGutsManager = notificationGutsManager;
         mNotificationEntryManager = notificationEntryManager;
@@ -73,6 +77,7 @@
         mNonBlockablePkgs = new HashSet<>();
         Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_nonBlockableNotificationPackages));
+        mGroupMembershipManager = groupMembershipManager;
     }
 
     /**
@@ -92,11 +97,12 @@
         // - The row is blockable (i.e. not non-blockable)
         // - The dismissed row is a valid group (>1 or 0 children from the same channel)
         // or the only child in the group
-        if ((row.getEntry().getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
+        final NotificationEntry entry = row.getEntry();
+        if ((entry.getUserSentiment() == USER_SENTIMENT_NEGATIVE || DEBUG)
                 && mIsShadeExpanded
                 && !row.getIsNonblockable()
-                && ((!row.isChildInGroup() || row.isOnlyChildInGroup())
-                        && row.getNumUniqueChannels() <= 1)) {
+                && ((!row.isChildInGroup() || mGroupMembershipManager.isOnlyChildInGroup(entry))
+                    && row.getNumUniqueChannels() <= 1)) {
             // Dismiss any current blocking helper before continuing forward (only one can be shown
             // at a given time).
             dismissCurrentBlockingHelper();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index c7e44c5..1de9308 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -28,9 +28,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -52,11 +50,10 @@
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationTemplateViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies;
 import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions;
 import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -122,8 +119,8 @@
     private int mSmallHeight;
     private int mHeadsUpHeight;
     private int mNotificationMaxHeight;
-    private StatusBarNotification mStatusBarNotification;
-    private NotificationGroupManager mGroupManager;
+    private NotificationEntry mNotificationEntry;
+    private GroupMembershipManager mGroupMembershipManager;
     private RemoteInputController mRemoteInputController;
     private Runnable mExpandedVisibleListener;
     private PeopleNotificationIdentifier mPeopleIdentifier;
@@ -778,7 +775,7 @@
     }
 
     private boolean isGroupExpanded() {
-        return mGroupManager.isGroupExpanded(mStatusBarNotification);
+        return mContainingNotification.isGroupExpanded();
     }
 
     public void setClipTopAmount(int clipTopAmount) {
@@ -908,10 +905,10 @@
     public int getBackgroundColorForExpansionState() {
         // When expanding or user locked we want the new type, when collapsing we want
         // the original type
-        final int visibleType = (mContainingNotification.isGroupExpanded()
-                || mContainingNotification.isUserLocked())
-                        ? calculateVisibleType()
-                        : getVisibleType();
+        final int visibleType = (
+                isGroupExpanded() || mContainingNotification.isUserLocked())
+                    ? calculateVisibleType()
+                    : getVisibleType();
         return getBackgroundColor(visibleType);
     }
 
@@ -1145,7 +1142,7 @@
     }
 
     public void onNotificationUpdated(NotificationEntry entry) {
-        mStatusBarNotification = entry.getSbn();
+        mNotificationEntry = entry;
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateAllSingleLineViews();
         ExpandableNotificationRow row = entry.getRow();
@@ -1176,7 +1173,7 @@
         if (mIsChildInGroup) {
             boolean isNewView = mSingleLineView == null;
             mSingleLineView = mHybridGroupManager.bindFromNotification(
-                    mSingleLineView, mContractedChild, mStatusBarNotification, this);
+                    mSingleLineView, mContractedChild, mNotificationEntry.getSbn(), this);
             if (isNewView) {
                 updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
                         mSingleLineView, mSingleLineView);
@@ -1363,7 +1360,7 @@
             return;
         }
         boolean isPersonWithShortcut =
-                mPeopleIdentifier.getPeopleNotificationType(entry.getSbn(), entry.getRanking())
+                mPeopleIdentifier.getPeopleNotificationType(entry)
                         >= PeopleNotificationIdentifier.TYPE_FULL_PERSON;
         boolean showButton = isBubblesEnabled()
                 && isPersonWithShortcut
@@ -1516,8 +1513,8 @@
         }
     }
 
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        mGroupManager = groupManager;
+    public void setGroupMembershipManager(GroupMembershipManager groupMembershipManager) {
+        mGroupMembershipManager = groupMembershipManager;
     }
 
     public void setRemoteInputController(RemoteInputController r) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 60074f6..7d418f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -52,7 +52,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -121,7 +121,7 @@
     private final INotificationManager mNotificationManager;
     private final LauncherApps mLauncherApps;
     private final ShortcutManager mShortcutManager;
-    private final CurrentUserContextTracker mContextTracker;
+    private final UserContextProvider mContextTracker;
     private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
     private final UiEventLogger mUiEventLogger;
 
@@ -138,7 +138,7 @@
             LauncherApps launcherApps,
             ShortcutManager shortcutManager,
             ChannelEditorDialogController channelEditorDialogController,
-            CurrentUserContextTracker contextTracker,
+            UserContextProvider contextTracker,
             Provider<PriorityOnboardingDialogController.Builder> builderProvider,
             AssistantFeedbackController assistantFeedbackController,
             BubbleController bubbleController,
@@ -484,7 +484,7 @@
                 onSettingsClick,
                 onSnoozeClickListener,
                 iconFactoryLoader,
-                mContextTracker.getCurrentUserContext(),
+                mContextTracker.getUserContext(),
                 mBuilderProvider,
                 mDeviceProvisionedController.isDeviceProvisioned(),
                 mMainHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 205cecc..65a72cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -24,7 +24,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -260,8 +259,7 @@
         }
         mFeedbackItem = createFeedbackItem(mContext);
         NotificationEntry entry = mParent.getEntry();
-        int personNotifType = mPeopleNotificationIdentifier
-                .getPeopleNotificationType(entry.getSbn(), entry.getRanking());
+        int personNotifType = mPeopleNotificationIdentifier.getPeopleNotificationType(entry);
         if (personNotifType == PeopleNotificationIdentifier.TYPE_PERSON) {
             mInfoItem = createPartialConversationItem(mContext);
         } else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index d5e5531..8050fea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -30,7 +30,6 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
 
@@ -50,7 +49,6 @@
     private ActivatableNotificationView mActivatedChild;
     private float mOverScrollTopAmount;
     private float mOverScrollBottomAmount;
-    private int mSpeedBumpIndex = -1;
     private boolean mDozing;
     private boolean mHideSensitive;
     private float mStackTranslation;
@@ -81,17 +79,17 @@
     private boolean mAppearing;
     private float mPulseHeight = MAX_PULSE_HEIGHT;
     private float mDozeAmount = 0.0f;
-    private HeadsUpManager mHeadUpManager;
     private Runnable mOnPulseHeightChangedListener;
     private ExpandableNotificationRow mTrackedHeadsUpRow;
     private float mAppearFraction;
 
+    /** Tracks the state from AlertingNotificationManager#hasNotifications() */
+    private boolean mHasAlertEntries;
+
     public AmbientState(
             Context context,
-            @NonNull SectionProvider sectionProvider,
-            HeadsUpManager headsUpManager) {
+            @NonNull SectionProvider sectionProvider) {
         mSectionProvider = sectionProvider;
-        mHeadUpManager = headsUpManager;
         reload(context);
     }
 
@@ -245,14 +243,6 @@
         return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
     }
 
-    public int getSpeedBumpIndex() {
-        return mSpeedBumpIndex;
-    }
-
-    public void setSpeedBumpIndex(int shelfIndex) {
-        mSpeedBumpIndex = shelfIndex;
-    }
-
     public SectionProvider getSectionProvider() {
         return mSectionProvider;
     }
@@ -393,7 +383,7 @@
     }
 
     public boolean hasPulsingNotifications() {
-        return mPulsing && mHeadUpManager != null && mHeadUpManager.hasNotifications();
+        return mPulsing && mHasAlertEntries;
     }
 
     public void setPulsing(boolean hasPulsing) {
@@ -408,10 +398,7 @@
     }
 
     public boolean isPulsing(NotificationEntry entry) {
-        if (!mPulsing || mHeadUpManager == null) {
-            return false;
-        }
-        return mHeadUpManager.isAlerting(entry.getKey());
+        return mPulsing && entry.isAlerting();
     }
 
     public boolean isPanelTracking() {
@@ -568,4 +555,8 @@
     public float getAppearFraction() {
         return mAppearFraction;
     }
+
+    public void setHasAlertEntries(boolean hasAlertEntries) {
+        mHasAlertEntries = hasAlertEntries;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index ff7793d..4f7e14b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -18,23 +18,21 @@
 import android.annotation.ColorInt
 import android.annotation.IntDef
 import android.annotation.LayoutRes
-import android.content.Intent
-import android.provider.Settings
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.media.KeyguardMediaController
-import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.people.DataListener
-import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter
-import com.android.systemui.statusbar.notification.people.PeopleHubViewBoundary
-import com.android.systemui.statusbar.notification.people.PersonViewModel
-import com.android.systemui.statusbar.notification.people.Subscription
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
+import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager
+import com.android.systemui.statusbar.notification.dagger.AlertingHeader
+import com.android.systemui.statusbar.notification.dagger.IncomingHeader
+import com.android.systemui.statusbar.notification.dagger.PeopleHeader
+import com.android.systemui.statusbar.notification.dagger.SilentHeader
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
@@ -46,19 +44,25 @@
 import javax.inject.Inject
 
 /**
- * Manages the boundaries of the two notification sections (high priority and low priority). Also
- * shows/hides the headers for those sections where appropriate.
+ * Manages the boundaries of the notification sections (incoming, conversations, high priority, and
+ * low priority).
+ *
+ * In the legacy notification pipeline, this is responsible for correctly positioning all section
+ * headers after the [NotificationStackScrollLayout] has had notifications added/removed/changed. In
+ * the new pipeline, this is handled as part of the [ShadeViewManager].
  *
  * TODO: Move remaining sections logic from NSSL into this class.
  */
 class NotificationSectionsManager @Inject internal constructor(
-    private val activityStarter: ActivityStarter,
     private val statusBarStateController: StatusBarStateController,
     private val configurationController: ConfigurationController,
-    private val peopleHubViewAdapter: PeopleHubViewAdapter,
     private val keyguardMediaController: KeyguardMediaController,
     private val sectionsFeatureManager: NotificationSectionsFeatureManager,
-    private val logger: NotificationSectionsLogger
+    private val logger: NotificationSectionsLogger,
+    @IncomingHeader private val incomingHeaderController: SectionHeaderController,
+    @PeopleHeader private val peopleHeaderController: SectionHeaderController,
+    @AlertingHeader private val alertingHeaderController: SectionHeaderController,
+    @SilentHeader private val silentHeaderController: SectionHeaderController
 ) : SectionProvider {
 
     private val configurationListener = object : ConfigurationController.ConfigurationListener {
@@ -67,46 +71,25 @@
         }
     }
 
-    private val peopleHubViewBoundary: PeopleHubViewBoundary = object : PeopleHubViewBoundary {
-        override fun setVisible(isVisible: Boolean) {
-            if (peopleHubVisible != isVisible) {
-                peopleHubVisible = isVisible
-                if (initialized) {
-                    updateSectionBoundaries("PeopleHub visibility changed")
-                }
-            }
-        }
-
-        override val associatedViewForClickAnimation: View
-            get() = peopleHeaderView!!
-
-        override val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
-            get() = peopleHeaderView!!.personViewAdapters
-    }
-
     private lateinit var parent: NotificationStackScrollLayout
     private var initialized = false
     private var onClearSilentNotifsClickListener: View.OnClickListener? = null
 
-    @get:VisibleForTesting
-    var silentHeaderView: SectionHeaderView? = null
-        private set
+    @VisibleForTesting
+    val silentHeaderView: SectionHeaderView?
+        get() = silentHeaderController.headerView
 
-    @get:VisibleForTesting
-    var alertingHeaderView: SectionHeaderView? = null
-        private set
+    @VisibleForTesting
+    val alertingHeaderView: SectionHeaderView?
+        get() = alertingHeaderController.headerView
 
-    @get:VisibleForTesting
-    var incomingHeaderView: SectionHeaderView? = null
-        private set
+    @VisibleForTesting
+    val incomingHeaderView: SectionHeaderView?
+        get() = incomingHeaderController.headerView
 
-    @get:VisibleForTesting
-    var peopleHeaderView: PeopleHubView? = null
-        private set
-
-    @set:VisibleForTesting
-    var peopleHubVisible = false
-    private var peopleHubSubscription: Subscription? = null
+    @VisibleForTesting
+    val peopleHeaderView: SectionHeaderView?
+        get() = peopleHeaderController.headerView
 
     @get:VisibleForTesting
     var mediaControlsView: MediaHeaderView? = null
@@ -150,34 +133,10 @@
      * Reinflates the entire notification header, including all decoration views.
      */
     fun reinflateViews(layoutInflater: LayoutInflater) {
-        silentHeaderView = reinflateView(
-                silentHeaderView, layoutInflater, R.layout.status_bar_notification_section_header
-        ).apply {
-            setHeaderText(R.string.notification_section_header_gentle)
-            setOnHeaderClickListener { onGentleHeaderClick() }
-            setOnClearAllClickListener { onClearGentleNotifsClick(it) }
-        }
-        alertingHeaderView = reinflateView(
-                alertingHeaderView, layoutInflater, R.layout.status_bar_notification_section_header
-        ).apply {
-            setHeaderText(R.string.notification_section_header_alerting)
-            setOnHeaderClickListener { onGentleHeaderClick() }
-        }
-        peopleHubSubscription?.unsubscribe()
-        peopleHubSubscription = null
-        peopleHeaderView = reinflateView(peopleHeaderView, layoutInflater, R.layout.people_strip)
-                .apply {
-                    setOnHeaderClickListener(View.OnClickListener { onGentleHeaderClick() })
-                }
-        if (ENABLE_SNOOZED_CONVERSATION_HUB) {
-            peopleHubSubscription = peopleHubViewAdapter.bindView(peopleHubViewBoundary)
-        }
-        incomingHeaderView = reinflateView(
-                incomingHeaderView, layoutInflater, R.layout.status_bar_notification_section_header
-        ).apply {
-            setHeaderText(R.string.notification_section_header_incoming)
-            setOnHeaderClickListener { onGentleHeaderClick() }
-        }
+        silentHeaderController.reinflateView(parent)
+        alertingHeaderController.reinflateView(parent)
+        peopleHeaderController.reinflateView(parent)
+        incomingHeaderController.reinflateView(parent)
         mediaControlsView =
                 reinflateView(mediaControlsView, layoutInflater, R.layout.keyguard_media_header)
                         .also(keyguardMediaController::attach)
@@ -296,7 +255,6 @@
         // target, but won't be once they are moved / removed after the pass has completed.
 
         val showHeaders = statusBarStateController.state != StatusBarState.KEYGUARD
-        val usingPeopleFiltering = sectionsFeatureManager.isFilteringEnabled()
         val usingMediaControls = sectionsFeatureManager.isMediaControlsEnabled()
 
         val mediaState = mediaControlsView?.let(::expandableViewHeaderState)
@@ -319,7 +277,6 @@
         ).filterNotNull()
 
         var peopleNotifsPresent = false
-        var lastNotifIndex = 0
         var nextBucket: Int? = null
         var inIncomingSection = false
 
@@ -373,30 +330,9 @@
 
             // Check if there are any people notifications
             peopleNotifsPresent = peopleNotifsPresent || row.entry.bucket == BUCKET_PEOPLE
-
-            if (nextBucket == null) {
-                lastNotifIndex = i
-            }
             nextBucket = row.entry.bucket
         }
 
-        if (showHeaders && usingPeopleFiltering && peopleHubVisible) {
-            peopleState?.targetPosition = peopleState?.targetPosition
-                    // Insert the people header even if there are no people visible, in order to
-                    // show the hub. Put it directly above the next header.
-                    ?: alertingState?.targetPosition
-                    ?: gentleState?.targetPosition
-                    // Put it at the end of the list.
-                    ?: lastNotifIndex
-
-            // Offset the target to account for the current position of the people header.
-            peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
-                peopleState.targetPosition?.let { target ->
-                    if (current < target) target - 1 else target
-                }
-            }
-        }
-
         mediaState?.targetPosition = if (usingMediaControls) 0 else null
 
         logger.logStr("New header target positions:")
@@ -420,14 +356,6 @@
                     .hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
             setAreThereDismissableGentleNotifs(hasActiveClearableNotifications)
         }
-        peopleHeaderView?.run {
-            canSwipe = showHeaders && peopleHubVisible && !peopleNotifsPresent
-            peopleState?.targetPosition?.let { targetPosition ->
-                if (targetPosition != peopleState.currentPosition) {
-                    resetTranslation()
-                }
-            }
-        }
     }
 
     private sealed class SectionBounds {
@@ -513,31 +441,13 @@
         }
     }
 
-    private fun onGentleHeaderClick() {
-        val intent = Intent(Settings.ACTION_NOTIFICATION_SETTINGS)
-        activityStarter.startActivity(
-                intent,
-                true,
-                true,
-                Intent.FLAG_ACTIVITY_SINGLE_TOP)
-    }
-
-    private fun onClearGentleNotifsClick(v: View) {
-        onClearSilentNotifsClickListener?.onClick(v)
-    }
-
     /** Listener for when the "clear all" button is clicked on the gentle notification header. */
     fun setOnClearSilentNotifsClickListener(listener: View.OnClickListener) {
         onClearSilentNotifsClickListener = listener
     }
 
-    fun hidePeopleRow() {
-        peopleHubVisible = false
-        updateSectionBoundaries("PeopleHub dismissed")
-    }
-
     fun setHeaderForegroundColor(@ColorInt color: Int) {
-        peopleHeaderView?.setTextColor(color)
+        peopleHeaderView?.setForegroundColor(color)
         silentHeaderView?.setForegroundColor(color)
         alertingHeaderView?.setForegroundColor(color)
     }
@@ -545,7 +455,6 @@
     companion object {
         private const val TAG = "NotifSectionsManager"
         private const val DEBUG = false
-        private const val ENABLE_SNOOZED_CONVERSATION_HUB = false
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b1a9efe..b33aa57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -92,8 +92,6 @@
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
@@ -102,7 +100,6 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
-import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -120,6 +117,8 @@
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -127,19 +126,12 @@
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -239,9 +231,10 @@
      * The algorithm which calculates the properties for our children
      */
     private final StackScrollAlgorithm mStackScrollAlgorithm;
-
     private final AmbientState mAmbientState;
-    private NotificationGroupManager mGroupManager;
+
+    private GroupMembershipManager mGroupMembershipManager;
+    private GroupExpansionManager mGroupExpansionManager;
     private NotificationActivityStarter mNotificationActivityStarter;
     private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
     private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
@@ -255,6 +248,9 @@
     private boolean mChangePositionInProgress;
     private boolean mChildTransferInProgress;
 
+    private int mSpeedBumpIndex = -1;
+    private boolean mSpeedBumpIndexDirty = true;
+
     /**
      * The raw amount of the overScroll on the top, which is not rubber-banded.
      */
@@ -340,13 +336,10 @@
     private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
-    private HeadsUpManagerPhone mHeadsUpManager;
     private final NotificationRoundnessManager mRoundnessManager;
     private boolean mTrackingHeadsUp;
-    private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
     private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
-    private FalsingManager mFalsingManager;
     private boolean mAnimationRunning;
     private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
             = new ViewTreeObserver.OnPreDrawListener() {
@@ -426,7 +419,6 @@
     };
     private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
     private boolean mPulsing;
-    private boolean mGroupExpandedForMeasure;
     private boolean mScrollable;
     private View mForcedScroll;
 
@@ -455,7 +447,6 @@
     private int mMaxDisplayedNotifications = -1;
     private int mStatusBarHeight;
     private int mMinInteractionHeight;
-    private boolean mNoAmbient;
     private final Rect mClipRect = new Rect();
     private boolean mIsClipped;
     private Rect mRequestedClipBounds;
@@ -499,7 +490,6 @@
     private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
     private NotificationPanelViewController mNotificationPanelController;
 
-    private final NotificationGutsManager mNotificationGutsManager;
     private final NotificationSectionsManager mSectionsManager;
     private final ForegroundServiceSectionController mFgsSectionController;
     private ForegroundServiceDungeonView mFgsSectionView;
@@ -513,6 +503,9 @@
     private NotificationStackScrollLayoutController mController;
 
     private boolean mKeyguardMediaControllorVisible;
+    private NotificationEntry mTopHeadsUpEntry;
+    private long mNumHeadsUp;
+    private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
 
     private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
             new ExpandableView.OnHeightChangedListener() {
@@ -562,9 +555,6 @@
             NotificationRoundnessManager notificationRoundnessManager,
             DynamicPrivacyController dynamicPrivacyController,
             SysuiStatusBarStateController statusbarStateController,
-            HeadsUpManagerPhone headsUpManager,
-            FalsingManager falsingManager,
-            NotificationGutsManager notificationGutsManager,
             NotificationSectionsManager notificationSectionsManager,
             ForegroundServiceSectionController fgsSectionController,
             ForegroundServiceDismissalFeatureController fgsFeatureController,
@@ -572,17 +562,14 @@
             NotifPipeline notifPipeline,
             NotificationEntryManager entryManager,
             NotifCollection notifCollection,
-            UiEventLogger uiEventLogger
+            UiEventLogger uiEventLogger,
+            GroupMembershipManager groupMembershipManager,
+            GroupExpansionManager groupExpansionManager
     ) {
         super(context, attrs, 0, 0);
         Resources res = getResources();
 
         mRoundnessManager = notificationRoundnessManager;
-
-        mNotificationGutsManager = notificationGutsManager;
-        mHeadsUpManager = headsUpManager;
-        mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
-        mFalsingManager = falsingManager;
         mFgsSectionController = fgsSectionController;
 
         mSectionsManager = notificationSectionsManager;
@@ -594,7 +581,7 @@
         });
         mSections = mSectionsManager.createSectionsForBuckets();
 
-        mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager);
+        mAmbientState = new AmbientState(context, mSectionsManager);
         mBgColor = context.getColor(R.color.notification_shade_background_color);
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
@@ -653,6 +640,8 @@
                 }
             });
         }
+        mGroupMembershipManager = groupMembershipManager;
+        mGroupExpansionManager = groupExpansionManager;
 
         mDynamicPrivacyController = dynamicPrivacyController;
         mStatusbarStateController = statusbarStateController;
@@ -750,27 +739,6 @@
         return false;
     }
 
-  @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-  public RemoteInputController.Delegate createDelegate() {
-        return new RemoteInputController.Delegate() {
-            public void setRemoteInputActive(NotificationEntry entry,
-                    boolean remoteInputActive) {
-                mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
-                entry.notifyHeightChanged(true /* needsAnimation */);
-                updateFooter();
-            }
-
-            public void lockScrollTo(NotificationEntry entry) {
-                NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
-            }
-
-            public void requestDisallowLongPressAndDismiss() {
-                requestDisallowLongPress();
-                requestDisallowDismiss();
-            }
-        };
-    }
-
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationSwipeActionHelper getSwipeActionHelper() {
         return mSwipeHelper;
@@ -956,7 +924,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private void updateBackgroundDimming() {
+    void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
         if (!mShouldDrawNotificationBackground) {
             return;
@@ -1043,6 +1011,36 @@
         return mAmbientState.isPulseExpanding();
     }
 
+    public int getSpeedBumpIndex() {
+        if (mSpeedBumpIndexDirty) {
+            mSpeedBumpIndexDirty = false;
+            int speedBumpIndex = 0;
+            int currentIndex = 0;
+            final int n = getChildCount();
+            for (int i = 0; i < n; i++) {
+                View view = getChildAt(i);
+                if (view.getVisibility() == View.GONE
+                        || !(view instanceof ExpandableNotificationRow)) {
+                    continue;
+                }
+                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+                currentIndex++;
+                boolean beforeSpeedBump;
+                if (mHighPriorityBeforeSpeedBump) {
+                    beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
+                } else {
+                    beforeSpeedBump = !row.getEntry().isAmbient();
+                }
+                if (beforeSpeedBump) {
+                    speedBumpIndex = currentIndex;
+                }
+            }
+
+            mSpeedBumpIndex = speedBumpIndex;
+        }
+        return mSpeedBumpIndex;
+    }
+
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
@@ -1096,12 +1094,6 @@
         }
     }
 
-    @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
-        mAmbientState.setSpeedBumpIndex(newIndex);
-        mNoAmbient = noAmbient;
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setChildLocationsChangedListener(
             NotificationLogger.OnChildLocationsChangedListener listener) {
@@ -1165,7 +1157,7 @@
         } else {
             mAmbientState.setScrollY(mOwnScrollY);
         }
-        mStackScrollAlgorithm.resetViewStates(mAmbientState);
+        mStackScrollAlgorithm.resetViewStates(mAmbientState, getSpeedBumpIndex());
         if (!isCurrentlyAnimating() && !mNeedsAnimation) {
             applyCurrentState();
         } else {
@@ -1472,14 +1464,13 @@
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getTopHeadsUpPinnedHeight() {
-        NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
-        if (topEntry == null) {
+        if (mTopHeadsUpEntry == null) {
             return 0;
         }
-        ExpandableNotificationRow row = topEntry.getRow();
+        ExpandableNotificationRow row = mTopHeadsUpEntry.getRow();
         if (row.isChildInGroup()) {
             final NotificationEntry groupSummary =
-                    mGroupManager.getGroupSummary(row.getEntry().getSbn());
+                    mGroupMembershipManager.getGroupSummary(row.getEntry());
             if (groupSummary != null) {
                 row = groupSummary.getRow();
             }
@@ -1497,7 +1488,7 @@
         int visibleNotifCount = getVisibleNotificationCount();
         if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
             if (isHeadsUpTransition()
-                    || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) {
+                    || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) {
                 if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) {
                     appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
                 }
@@ -1655,10 +1646,8 @@
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
                     NotificationEntry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().getRow() != row
-                            && mGroupManager.getGroupSummary(
-                            mHeadsUpManager.getTopEntry().getSbn())
-                            != entry) {
+                            && mTopHeadsUpEntry.getRow() != row
+                            && mGroupMembershipManager.getGroupSummary(mTopHeadsUpEntry) != entry) {
                         continue;
                     }
                     return row.getViewAtPosition(touchY - childTop);
@@ -2285,7 +2274,7 @@
         // In current design, it only use the top HUN to treat all of HUNs
         // although there are more than one HUNs
         int contentHeight = mContentHeight;
-        if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
+        if (!isExpanded() && mInHeadsUpPinnedMode) {
             contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
         }
         int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
@@ -2636,7 +2625,7 @@
                     false /* shiftPulsingWithFirst */);
             minTopPosition = firstVisibleSection.getBounds().top;
         }
-        boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1
+        boolean shiftPulsingWithFirst = mNumHeadsUp <= 1
                 && (mAmbientState.isDozing()
                         || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
         for (NotificationSection section : mSections) {
@@ -3056,8 +3045,8 @@
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
     private boolean isChildInGroup(View child) {
         return child instanceof ExpandableNotificationRow
-                && mGroupManager.isChildInGroupWithSummary(
-                ((ExpandableNotificationRow) child).getEntry().getSbn());
+                && mGroupMembershipManager.isChildInGroup(
+                ((ExpandableNotificationRow) child).getEntry());
     }
 
     /**
@@ -3122,6 +3111,8 @@
         return hasAddEvent;
     }
 
+    // TODO (b/162832756): remove since this won't happen in new pipeline (we prune groups in
+    //  ShadeListBuilder)
     /**
      * @param child the child to query
      * @return whether a view is not a top level child but a child notification and that group is
@@ -3132,7 +3123,7 @@
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             NotificationEntry groupSummary =
-                    mGroupManager.getGroupSummary(row.getEntry().getSbn());
+                    mGroupMembershipManager.getGroupSummary(row.getEntry());
             if (groupSummary != null && groupSummary.getRow() != row) {
                 return row.getVisibility() == View.INVISIBLE;
             }
@@ -3511,7 +3502,7 @@
                     // Only animate if we still have pinned heads up, otherwise we just have the
                     // regular collapse animation of the lock screen
                     || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()
-                            && mHeadsUpManager.hasPinnedHeadsUp());
+                            && mInHeadsUpPinnedMode);
             if (performDisappearAnimation && !isHeadsUp) {
                 type = row.wasJustClicked()
                         ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
@@ -3754,58 +3745,16 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onTouchEvent(MotionEvent ev) {
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
-                || ev.getActionMasked() == MotionEvent.ACTION_UP;
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        boolean swipingInProgress = mSwipingInProgress;
-        if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
-            if (isCancelOrUp) {
-                mExpandHelper.onlyObserveMovements(false);
-            }
-            boolean wasExpandingBefore = mExpandingNotification;
-            expandWantsIt = mExpandHelper.onTouchEvent(ev);
-            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
-                    && !mDisallowScrollingInThisMotion) {
-                dispatchDownEventToScroller(ev);
-            }
-        }
-        boolean scrollerWantsIt = false;
-        if (mIsExpanded && !swipingInProgress && !mExpandingNotification
-                && !mDisallowScrollingInThisMotion) {
-            scrollerWantsIt = onScrollTouch(ev);
-        }
-        boolean horizontalSwipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+        if (mTouchHandler != null && mTouchHandler.onTouchEvent(ev)) {
+            return true;
         }
 
-        // Check if we need to clear any snooze leavebehinds
-        if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
-                && guts.getGutsContent() instanceof NotificationSnooze) {
-            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
-            if ((ns.isExpanded() && isCancelOrUp)
-                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
-                // If the leavebehind is expanded we clear it on the next up event, otherwise we
-                // clear it on the next non-horizontal swipe or expand event.
-                checkSnoozeLeavebehind();
-            }
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+        return super.onTouchEvent(ev);
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void dispatchDownEventToScroller(MotionEvent ev) {
+    void dispatchDownEventToScroller(MotionEvent ev) {
         MotionEvent downEvent = MotionEvent.obtain(ev);
         downEvent.setAction(MotionEvent.ACTION_DOWN);
         onScrollTouch(downEvent);
@@ -3854,7 +3803,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private boolean onScrollTouch(MotionEvent ev) {
+    boolean onScrollTouch(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
         }
@@ -3943,7 +3892,7 @@
                         customOverScrollBy((int) scrollAmount, mOwnScrollY,
                                 range, getHeight() / 2);
                         // If we're scrolling, leavebehinds should be dismissed
-                        checkSnoozeLeavebehind();
+                        mController.checkSnoozeLeavebehind();
                     }
                 }
                 break;
@@ -4061,44 +4010,14 @@
     @Override
     @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        initDownStates(ev);
-        handleEmptySpaceClick(ev);
-
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        boolean expandWantsIt = false;
-        boolean swipingInProgress = mSwipingInProgress;
-        if (!swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
-            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+        if (mTouchHandler != null && mTouchHandler.onInterceptTouchEvent(ev)) {
+            return true;
         }
-        boolean scrollWantsIt = false;
-        if (!swipingInProgress && !mExpandingNotification) {
-            scrollWantsIt = onInterceptTouchEventScroll(ev);
-        }
-        boolean swipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
-        }
-        // Check if we need to clear any snooze leavebehinds
-        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-        if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
-                !expandWantsIt && !scrollWantsIt) {
-            mCheckForLeavebehind = false;
-            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+        return super.onInterceptTouchEvent(ev);
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void handleEmptySpaceClick(MotionEvent ev) {
+    void handleEmptySpaceClick(MotionEvent ev) {
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_MOVE:
                 final float touchSlop = getTouchSlop(ev);
@@ -4117,7 +4036,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void initDownStates(MotionEvent ev) {
+    void initDownStates(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mExpandedInThisMotion = false;
             mOnlyScrollingInThisMotion = !mScroller.isFinished();
@@ -4139,7 +4058,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private boolean onInterceptTouchEventScroll(MotionEvent ev) {
+    boolean onInterceptTouchEventScroll(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
         }
@@ -4335,29 +4254,6 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void closeControlsIfOutsideTouch(MotionEvent ev) {
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
-        View translatingParentView = mSwipeHelper.getTranslatingParentView();
-        View view = null;
-        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
-            // Only close visible guts if they're not a leavebehind.
-            view = guts;
-        } else if (menuRow != null && menuRow.isMenuVisible()
-                && translatingParentView != null) {
-            // Checking menu
-            view = translatingParentView;
-        }
-        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
-            // Touch was outside visible guts / menu notification, close what's visible
-            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
-                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            resetExposedMenuView(true /* animate */, true /* force */);
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
     void setSwipingInProgress(boolean swiping) {
         mSwipingInProgress = swiping;
         if (swiping) {
@@ -4392,32 +4288,15 @@
         return Math.max(mMaxLayoutHeight - mContentHeight, 0);
     }
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    void checkSnoozeLeavebehind() {
-        if (mCheckForLeavebehind) {
-            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            mCheckForLeavebehind = false;
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    void resetCheckSnoozeLeavebehind() {
-        mCheckForLeavebehind = true;
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     void onExpansionStarted() {
         mIsExpansionChanging = true;
         mAmbientState.setExpansionChanging(true);
-        checkSnoozeLeavebehind();
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     void onExpansionStopped() {
         mIsExpansionChanging = false;
-        resetCheckSnoozeLeavebehind();
         mAmbientState.setExpansionChanging(false);
         if (!mIsExpanded) {
             resetScrollPosition();
@@ -4500,7 +4379,7 @@
         if (changed) {
             mWillExpand = false;
             if (!mIsExpanded) {
-                mGroupManager.collapseAllGroups();
+                mGroupExpansionManager.collapseGroups();
                 mExpandHelper.cancelImmediately();
             }
             updateNotificationAnimationStates();
@@ -5037,12 +4916,6 @@
         this.mStatusBar = statusBar;
     }
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        this.mGroupManager = groupManager;
-        mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener);
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     void requestAnimateEverything() {
         if (mIsExpanded && mAnimationsEnabled) {
@@ -5141,17 +5014,35 @@
     public void removeContainerView(View v) {
         Assert.isMainThread();
         removeView(v);
+        if (v instanceof ExpandableNotificationRow && !mController.isShowingEmptyShadeView()) {
+            mController.updateShowEmptyShadeView();
+            updateFooter();
+        }
+
+        updateSpeedBumpIndex();
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void addContainerView(View v) {
         Assert.isMainThread();
         addView(v);
+        if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
+            mController.updateShowEmptyShadeView();
+            updateFooter();
+        }
+
+        updateSpeedBumpIndex();
     }
 
     public void addContainerViewAt(View v, int index) {
         Assert.isMainThread();
         addView(v, index);
+        if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
+            mController.updateShowEmptyShadeView();
+            updateFooter();
+        }
+
+        updateSpeedBumpIndex();
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5202,12 +5093,6 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setScrimController(ScrimController scrimController) {
-        mScrimController = scrimController;
-        mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void forceNoOverlappingRendering(boolean force) {
         mForceNoOverlappingRendering = force;
     }
@@ -5258,6 +5143,10 @@
         updateScrollability();
     }
 
+    boolean isQsExpanded() {
+        return mQsExpanded;
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setQsExpansionFraction(float qsExpansionFraction) {
         mQsExpansionFraction = qsExpansionFraction;
@@ -5380,6 +5269,7 @@
     protected void setStatusBarState(int statusBarState) {
         mStatusBarState = statusBarState;
         mAmbientState.setStatusBarState(statusBarState);
+        updateSpeedBumpIndex();
     }
 
     void onStatePostChange(boolean fromShadeLocked) {
@@ -5811,23 +5701,6 @@
         mCurrentUserId = userId;
     }
 
-    void onMenuShown(View row) {
-        mSwipeHelper.onMenuShown(row);
-    }
-
-    void onMenuReset(View row) {
-        View translatingParentView = mSwipeHelper.getTranslatingParentView();
-        if (translatingParentView != null && row == translatingParentView) {
-            mSwipeHelper.clearExposedMenuView();
-            mSwipeHelper.clearTranslatingParentView();
-            if (row instanceof ExpandableNotificationRow) {
-                mHeadsUpManager.setMenuShown(
-                        ((ExpandableNotificationRow) row).getEntry(), false);
-
-            }
-        }
-    }
-
     void addSwipedOutView(View v) {
         mSwipedOutViews.add(v);
     }
@@ -5840,6 +5713,67 @@
         mAmbientState.onDragFinished(view);
     }
 
+    void setTopHeadsUpEntry(NotificationEntry topEntry) {
+        mTopHeadsUpEntry = topEntry;
+    }
+
+    void setNumHeadsUp(long numHeadsUp) {
+        mNumHeadsUp = numHeadsUp;
+        mAmbientState.setHasAlertEntries(numHeadsUp > 0);
+    }
+
+    boolean getSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    public boolean getIsExpanded() {
+        return mIsExpanded;
+    }
+
+    boolean getOnlyScrollingInThisMotion() {
+        return mOnlyScrollingInThisMotion;
+    }
+
+    ExpandHelper getExpandHelper() {
+        return mExpandHelper;
+    }
+
+    boolean isExpandingNotification() {
+        return mExpandingNotification;
+    }
+
+    boolean getDisallowScrollingInThisMotion() {
+        return mDisallowScrollingInThisMotion;
+    }
+
+    boolean isBeingDragged() {
+        return mIsBeingDragged;
+    }
+
+    boolean getExpandedInThisMotion() {
+        return mExpandedInThisMotion;
+    }
+
+    boolean getDisallowDismissInThisMotion() {
+        return mDisallowDismissInThisMotion;
+    }
+
+    void setCheckForLeaveBehind(boolean checkForLeaveBehind) {
+        mCheckForLeavebehind = checkForLeaveBehind;
+    }
+
+    void setTouchHandler(NotificationStackScrollLayoutController.TouchHandler touchHandler) {
+        mTouchHandler = touchHandler;
+    }
+
+    boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    boolean getCheckSnoozeLeaveBehind() {
+        return mCheckForLeavebehind;
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
@@ -5873,32 +5807,11 @@
      * @param open     Should the fling open or close the overscroll view.
      */
     void flingTopOverscroll(float velocity, boolean open);
-  }
+    }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void updateSpeedBumpIndex() {
-        int speedBumpIndex = 0;
-        int currentIndex = 0;
-        final int N = getChildCount();
-        for (int i = 0; i < N; i++) {
-            View view = getChildAt(i);
-            if (view.getVisibility() == View.GONE || !(view instanceof ExpandableNotificationRow)) {
-                continue;
-            }
-            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            currentIndex++;
-            boolean beforeSpeedBump;
-            if (mHighPriorityBeforeSpeedBump) {
-                beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
-            } else {
-                beforeSpeedBump = !row.getEntry().isAmbient();
-            }
-            if (beforeSpeedBump) {
-                speedBumpIndex = currentIndex;
-            }
-        }
-        boolean noAmbient = speedBumpIndex == N;
-        updateSpeedBumpIndex(speedBumpIndex, noAmbient);
+    private void updateSpeedBumpIndex() {
+        mSpeedBumpIndexDirty = true;
     }
 
     /** Updates the indices of the boundaries between sections. */
@@ -5935,7 +5848,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    void resetExposedMenuView(boolean animate, boolean force) {
+    private void resetExposedMenuView(boolean animate, boolean force) {
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
@@ -6293,6 +6206,10 @@
         mKeyguardMediaControllorVisible = keyguardMediaControllorVisible;
     }
 
+    void resetCheckSnoozeLeavebehind() {
+        setCheckForLeaveBehind(true);
+    }
+
     // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
@@ -6345,7 +6262,7 @@
         @Override
         public void onTouchSlopExceeded() {
             cancelLongPress();
-            checkSnoozeLeavebehind();
+            mController.checkSnoozeLeavebehind();
         }
 
         @Override
@@ -6405,39 +6322,22 @@
 
     public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
 
+    void onGroupExpandChanged(ExpandableNotificationRow changedRow, boolean expanded) {
+        boolean animated = mAnimationsEnabled && (mIsExpanded || changedRow.isPinned());
+        if (animated) {
+            mExpandedGroupView = changedRow;
+            mNeedsAnimation = true;
+        }
+        changedRow.setChildrenExpanded(expanded, animated);
+        onChildHeightChanged(changedRow, false /* needsAnimation */);
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
-        @Override
-        public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
-            boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
-                    && (mIsExpanded || changedRow.isPinned());
-            if (animated) {
-                mExpandedGroupView = changedRow;
-                mNeedsAnimation = true;
+        runAfterAnimationFinished(new Runnable() {
+            @Override
+            public void run() {
+                changedRow.onFinishedExpansionChange();
             }
-            changedRow.setChildrenExpanded(expanded, animated);
-            if (!mGroupExpandedForMeasure) {
-                onChildHeightChanged(changedRow, false /* needsAnimation */);
-            }
-            runAfterAnimationFinished(new Runnable() {
-                @Override
-                public void run() {
-                    changedRow.onFinishedExpansionChange();
-                }
-            });
-        }
-
-        @Override
-        public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
-            mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
-        }
-
-        @Override
-        public void onGroupsChanged() {
-            mStatusBar.requestNotificationUpdate("onGroupsChanged");
-        }
-    };
+        });
+    }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 73b4cad..c10362d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -17,6 +17,9 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
 
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -34,6 +37,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -54,22 +59,30 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
+import com.android.systemui.statusbar.notification.dagger.SilentHeader;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 
@@ -100,6 +113,7 @@
     private final NotificationSectionsManager mNotificationSectionsManager;
     private final Resources mResources;
     private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
+    private final ScrimController mScrimController;
     private final KeyguardMediaController mKeyguardMediaController;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final KeyguardBypassController mKeyguardBypassController;
@@ -107,9 +121,13 @@
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     // TODO: StatusBar should be encapsulated behind a Controller
     private final StatusBar mStatusBar;
+    private final SectionHeaderController mSilentHeaderController;
 
     private NotificationStackScrollLayout mView;
     private boolean mFadeNotificationsOnDismiss;
+    private NotificationSwipeHelper mSwipeHelper;
+    private boolean mShowEmptyShadeView;
+    private int mBarState;
 
     private final NotificationListContainerImpl mNotificationListContainer =
             new NotificationListContainerImpl();
@@ -120,6 +138,8 @@
                 @Override
                 public void onViewAttachedToWindow(View v) {
                     mConfigurationController.addCallback(mConfigurationListener);
+                    mZenModeController.addCallback(mZenModeControllerCallback);
+                    mBarState = mStatusBarStateController.getState();
                     mStatusBarStateController.addCallback(
                             mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER);
                 }
@@ -127,6 +147,7 @@
                 @Override
                 public void onViewDetachedFromWindow(View v) {
                     mConfigurationController.removeCallback(mConfigurationListener);
+                    mZenModeController.removeCallback(mZenModeControllerCallback);
                     mStatusBarStateController.removeCallback(mStateListener);
                 }
             };
@@ -147,11 +168,13 @@
     final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
         @Override
         public void onDensityOrFontScaleChanged() {
+            updateShowEmptyShadeView();
             mView.reinflateViews();
         }
 
         @Override
         public void onOverlayChanged() {
+            updateShowEmptyShadeView();
             mView.updateCornerRadius();
             mView.reinflateViews();
         }
@@ -172,14 +195,15 @@
                 @Override
                 public void onStatePreChange(int oldState, int newState) {
                     if (oldState == StatusBarState.SHADE_LOCKED
-                            && newState == StatusBarState.KEYGUARD) {
+                            && newState == KEYGUARD) {
                         mView.requestAnimateEverything();
                     }
                 }
 
                 @Override
                 public void onStateChanged(int newState) {
-                    mView.setStatusBarState(newState);
+                    mBarState = newState;
+                    mView.setStatusBarState(mBarState);
                 }
 
                 @Override
@@ -217,7 +241,16 @@
 
         @Override
         public void onMenuReset(View row) {
-            mView.onMenuReset(row);
+            View translatingParentView = mSwipeHelper.getTranslatingParentView();
+            if (translatingParentView != null && row == translatingParentView) {
+                mSwipeHelper.clearExposedMenuView();
+                mSwipeHelper.clearTranslatingParentView();
+                if (row instanceof ExpandableNotificationRow) {
+                    mHeadsUpManager.setMenuShown(
+                            ((ExpandableNotificationRow) row).getEntry(), false);
+
+                }
+            }
         }
 
         @Override
@@ -228,7 +261,7 @@
                         .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
                         .setType(MetricsEvent.TYPE_ACTION));
                 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
-                mView.onMenuShown(row);
+                mSwipeHelper.onMenuShown(row);
                 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
                         false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
                         false /* resetMenu */);
@@ -246,7 +279,7 @@
                     }
 
                     // Close the menu row since we went directly to the guts
-                    mView.resetExposedMenuView(false, true);
+                    mSwipeHelper.resetExposedMenuView(false, true);
                 }
             }
         }
@@ -331,10 +364,6 @@
                                 row.performDismissWithBlockingHelper(false /* fromAccessibility */);
                     }
 
-                    if (view instanceof PeopleHubView) {
-                        mNotificationSectionsManager.hidePeopleRow();
-                    }
-
                     if (!isBlockingHelperShown) {
                         mView.addSwipedOutView(view);
                     }
@@ -438,7 +467,39 @@
                 }
             };
 
-    private NotificationSwipeHelper mSwipeHelper;
+    private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
+            new OnHeadsUpChangedListener() {
+        @Override
+        public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+            mView.setInHeadsUpPinnedMode(inPinnedMode);
+        }
+
+        @Override
+        public void onHeadsUpPinned(NotificationEntry entry) {
+
+        }
+
+        @Override
+        public void onHeadsUpUnPinned(NotificationEntry entry) {
+
+        }
+
+        @Override
+        public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+            long numEntries = mHeadsUpManager.getAllEntries().count();
+            NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
+            mView.setNumHeadsUp(numEntries);
+            mView.setTopHeadsUpEntry(topEntry);
+        }
+    };
+
+    private final ZenModeController.Callback mZenModeControllerCallback =
+            new ZenModeController.Callback() {
+                @Override
+                public void onZenChanged(int zen) {
+                    updateShowEmptyShadeView();
+                }
+            };
 
     @Inject
     public NotificationStackScrollLayoutController(
@@ -460,7 +521,11 @@
             NotificationSectionsManager notificationSectionsManager,
             @Main Resources resources,
             NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
-            StatusBar statusBar) {
+            StatusBar statusBar,
+            ScrimController scrimController,
+            NotificationGroupManagerLegacy legacyGroupManager,
+            GroupExpansionManager groupManager,
+            @SilentHeader SectionHeaderController silentHeaderController) {
         mAllowLongPress = allowLongPress;
         mNotificationGutsManager = notificationGutsManager;
         mHeadsUpManager = headsUpManager;
@@ -480,11 +545,30 @@
         mResources = resources;
         mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
         mStatusBar = statusBar;
+        mScrimController = scrimController;
+        groupManager.registerGroupExpansionChangeListener((changedRow, expanded) -> {
+            mView.onGroupExpandChanged(changedRow, expanded);
+        });
+
+        legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
+            @Override
+            public void onGroupCreatedFromChildren(NotificationGroup group) {
+                mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
+            }
+
+            @Override
+            public void onGroupsChanged() {
+                mStatusBar.requestNotificationUpdate("onGroupsChanged");
+            }
+        });
+        mSilentHeaderController = silentHeaderController;
     }
 
     public void attach(NotificationStackScrollLayout view) {
         mView = view;
         mView.setController(this);
+        mView.setTouchHandler(new TouchHandler());
+        mView.setStatusBar(mStatusBar);
 
         mSwipeHelper = mNotificationSwipeHelperBuilder
                 .setSwipeDirection(SwipeHelper.X)
@@ -496,8 +580,12 @@
                 mSwipeHelper);
 
         mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here?
+        mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+        mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
         mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
 
+        mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
+
         mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
         mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId());
 
@@ -539,6 +627,7 @@
             mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
         }
         mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+        mSilentHeaderController.setOnClearAllClickListener(v -> clearSilentNotifications());
     }
 
     public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
@@ -628,6 +717,10 @@
         return mView.getWakeUpHeight();
     }
 
+    public int getSpeedBumpIndex() {
+        return mView.getSpeedBumpIndex();
+    }
+
     public void setHideAmount(float linearAmount, float amount) {
         mView.setHideAmount(linearAmount, amount);
     }
@@ -738,11 +831,17 @@
     }
 
     public void checkSnoozeLeavebehind() {
-        mView.checkSnoozeLeavebehind();
+        if (mView.getCheckSnoozeLeaveBehind()) {
+            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            mView.setCheckForLeaveBehind(false);
+        }
     }
 
     public void setQsExpanded(boolean expanded) {
         mView.setQsExpanded(expanded);
+        updateShowEmptyShadeView();
     }
 
     public void setScrollingEnabled(boolean enabled) {
@@ -761,10 +860,6 @@
         mView.updateTopPadding(qsHeight, animate);
     }
 
-    public void resetCheckSnoozeLeavebehind() {
-        mView.resetCheckSnoozeLeavebehind();
-    }
-
     public boolean isScrolledToBottom() {
         return mView.isScrolledToBottom();
     }
@@ -815,9 +910,11 @@
 
     public void onExpansionStarted() {
         mView.onExpansionStarted();
+        checkSnoozeLeavebehind();
     }
 
     public void onExpansionStopped() {
+        mView.setCheckForLeaveBehind(false);
         mView.onExpansionStopped();
     }
 
@@ -853,8 +950,21 @@
         return mView.getFooterViewHeightWithPadding();
     }
 
-    public void updateEmptyShadeView(boolean visible) {
-        mView.updateEmptyShadeView(visible, mZenModeController.areNotificationsHiddenInShade());
+    /**
+     * Update whether we should show the empty shade view (no notifications in the shade).
+     * If so, send the update to our view.
+     */
+    public void updateShowEmptyShadeView() {
+        mShowEmptyShadeView = mBarState != KEYGUARD
+                && !mView.isQsExpanded()
+                && mView.getVisibleNotificationCount() == 0;
+        mView.updateEmptyShadeView(
+                mShowEmptyShadeView,
+                mZenModeController.areNotificationsHiddenInShade());
+    }
+
+    public boolean isShowingEmptyShadeView() {
+        return mShowEmptyShadeView;
     }
 
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
@@ -910,17 +1020,29 @@
     }
 
     public RemoteInputController.Delegate createDelegate() {
-        return mView.createDelegate();
+        return new RemoteInputController.Delegate() {
+            public void setRemoteInputActive(NotificationEntry entry,
+                    boolean remoteInputActive) {
+                mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
+                entry.notifyHeightChanged(true /* needsAnimation */);
+                updateFooter();
+            }
+
+            public void lockScrollTo(NotificationEntry entry) {
+                mView.lockScrollTo(entry.getRow());
+            }
+
+            public void requestDisallowLongPressAndDismiss() {
+                mView.requestDisallowLongPress();
+                mView.requestDisallowDismiss();
+            }
+        };
     }
 
     public void updateSectionBoundaries(String reason) {
         mView.updateSectionBoundaries(reason);
     }
 
-    public void updateSpeedBumpIndex() {
-        mView.updateSpeedBumpIndex();
-    }
-
     public void updateFooter() {
         mView.updateFooter();
     }
@@ -946,30 +1068,14 @@
         mView.setNotificationPanelController(notificationPanelViewController);
     }
 
-    public void setStatusBar(StatusBar statusBar) {
-        mView.setStatusBar(statusBar);
-    }
-
-    public void setGroupManager(NotificationGroupManager groupManager) {
-        mView.setGroupManager(groupManager);
-    }
-
     public void setShelfController(NotificationShelfController notificationShelfController) {
         mView.setShelfController(notificationShelfController);
     }
 
-    public void setScrimController(ScrimController scrimController) {
-        mView.setScrimController(scrimController);
-    }
-
     public ExpandableView getFirstChildNotGone() {
         return mView.getFirstChildNotGone();
     }
 
-    public void setInHeadsUpPinnedMode(boolean inPinnedMode) {
-        mView.setInHeadsUpPinnedMode(inPinnedMode);
-    }
-
     public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
         mView.generateHeadsUpAnimation(entry, isHeadsUp);
     }
@@ -1002,7 +1108,7 @@
         return mView.calculateGapHeight(previousView, child, count);
     }
 
-    public NotificationRoundnessManager getNoticationRoundessManager() {
+    NotificationRoundnessManager getNoticationRoundessManager() {
         return mNotificationRoundnessManager;
     }
 
@@ -1010,7 +1116,40 @@
         return mNotificationListContainer;
     }
 
+    public void resetCheckSnoozeLeavebehind() {
+        mView.resetCheckSnoozeLeavebehind();
+    }
+
+    public void closeControlsIfOutsideTouch(MotionEvent ev) {
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+        View translatingParentView = mSwipeHelper.getTranslatingParentView();
+        View view = null;
+        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+            // Only close visible guts if they're not a leavebehind.
+            view = guts;
+        } else if (menuRow != null && menuRow.isMenuVisible()
+                && translatingParentView != null) {
+            // Checking menu
+            view = translatingParentView;
+        }
+        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
+            // Touch was outside visible guts / menu notification, close what's visible
+            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+        }
+    }
+
+    public void clearSilentNotifications() {
+        // Leave the shade open if there will be other notifs left over to clear
+        final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
+        mView.clearNotifications(ROWS_GENTLE, closeShade);
+    }
+
     private class NotificationListContainerImpl implements NotificationListContainer {
+
         @Override
         public void setChildTransferInProgress(boolean childTransferInProgress) {
             mView.setChildTransferInProgress(childTransferInProgress);
@@ -1084,7 +1223,7 @@
 
         @Override
         public void resetExposedMenuView(boolean animate, boolean force) {
-            mView.resetExposedMenuView(animate, force);
+            mSwipeHelper.resetExposedMenuView(animate, force);
         }
 
         @Override
@@ -1148,4 +1287,99 @@
             mView.setWillExpand(willExpand);
         }
     }
+
+    class TouchHandler implements Gefingerpoken {
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            mView.initDownStates(ev);
+            mView.handleEmptySpaceClick(ev);
+
+            NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+            boolean expandWantsIt = false;
+            boolean swipingInProgress = mView.isSwipingInProgress();
+            if (!swipingInProgress && !mView.getOnlyScrollingInThisMotion() && guts == null) {
+                expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
+            }
+            boolean scrollWantsIt = false;
+            if (!swipingInProgress && !mView.isExpandingNotification()) {
+                scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
+            }
+            boolean swipeWantsIt = false;
+            if (!mView.isBeingDragged()
+                    && !mView.isExpandingNotification()
+                    && !mView.getExpandedInThisMotion()
+                    && !mView.getOnlyScrollingInThisMotion()
+                    && !mView.getDisallowDismissInThisMotion()) {
+                swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+            }
+            // Check if we need to clear any snooze leavebehinds
+            boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+            if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
+                    !expandWantsIt && !scrollWantsIt) {
+                mView.setCheckForLeaveBehind(false);
+                mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+                        false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                        false /* resetMenu */);
+            }
+            if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+                mView.setCheckForLeaveBehind(true);
+            }
+            return swipeWantsIt || scrollWantsIt || expandWantsIt;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+            boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+                    || ev.getActionMasked() == MotionEvent.ACTION_UP;
+            mView.handleEmptySpaceClick(ev);
+            boolean expandWantsIt = false;
+            boolean swipingInProgress = mView.getSwipingInProgress();
+            boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
+            boolean expandingNotification = mView.isExpandingNotification();
+            if (mView.getIsExpanded() && !swipingInProgress && !onlyScrollingInThisMotion
+                    && guts == null) {
+                ExpandHelper expandHelper = mView.getExpandHelper();
+                if (isCancelOrUp) {
+                    expandHelper.onlyObserveMovements(false);
+                }
+                boolean wasExpandingBefore = expandingNotification;
+                expandWantsIt = expandHelper.onTouchEvent(ev);
+                expandingNotification = mView.isExpandingNotification();
+                if (mView.getExpandedInThisMotion() && !expandingNotification && wasExpandingBefore
+                        && !mView.getDisallowScrollingInThisMotion()) {
+                    mView.dispatchDownEventToScroller(ev);
+                }
+            }
+            boolean scrollerWantsIt = false;
+            if (mView.isExpanded() && !swipingInProgress && !expandingNotification
+                    && !mView.getDisallowScrollingInThisMotion()) {
+                scrollerWantsIt = mView.onScrollTouch(ev);
+            }
+            boolean horizontalSwipeWantsIt = false;
+            if (!mView.isBeingDragged()
+                    && !expandingNotification
+                    && !mView.getExpandedInThisMotion()
+                    && !onlyScrollingInThisMotion
+                    && !mView.getDisallowDismissInThisMotion()) {
+                horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+            }
+
+            // Check if we need to clear any snooze leavebehinds
+            if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
+                    && guts.getGutsContent() instanceof NotificationSnooze) {
+                NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+                if ((ns.isExpanded() && isCancelOrUp)
+                        || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+                    // If the leavebehind is expanded we clear it on the next up event, otherwise we
+                    // clear it on the next non-horizontal swipe or expand event.
+                    checkSnoozeLeavebehind();
+                }
+            }
+            if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+                mView.setCheckForLeaveBehind(true);
+            }
+            return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 1e80e88..ba01c84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -227,7 +227,7 @@
                 || (isFastNonDismissGesture && isAbleToShowMenu);
         int menuSnapTarget = menuRow.getMenuSnapTarget();
         boolean isNonFalseMenuRevealingGesture =
-                !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
+                !isFalseGesture() && isMenuRevealingGestureAwayFromMenu;
         if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
                 && menuSnapTarget != 0) {
             // Menu has not been snapped to previously and this is menu revealing gesture
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index 5777ba1..99ec7548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -97,7 +97,7 @@
     /**
      * Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything).
      */
-    void setOnHeaderClickListener(View.OnClickListener listener) {
+    public void setOnHeaderClickListener(View.OnClickListener listener) {
         mLabelClickListener = listener;
         mLabelView.setOnClickListener(listener);
     }
@@ -112,7 +112,7 @@
     }
 
     /** Fired when the user clicks on the "X" button on the far right of the header. */
-    void setOnClearAllClickListener(View.OnClickListener listener) {
+    public void setOnClearAllClickListener(View.OnClickListener listener) {
         mOnClearClickListener = listener;
         mClearAllButton.setOnClickListener(listener);
     }
@@ -122,7 +122,8 @@
         return true;
     }
 
-    void setHeaderText(@StringRes int resId) {
+    /** Sets text to be displayed in the header */
+    public void setHeaderText(@StringRes int resId) {
         mLabelTextId = resId;
         mLabelView.setText(resId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 541c784..95edfe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -91,7 +91,7 @@
     /**
      * Updates the state of all children in the hostview based on this algorithm.
      */
-    public void resetViewStates(AmbientState ambientState) {
+    public void resetViewStates(AmbientState ambientState, int speedBumpIndex) {
         // The state of the local variables are saved in an algorithmState to easily subdivide it
         // into multiple phases.
         StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
@@ -110,7 +110,7 @@
 
         updateDimmedActivatedHideSensitive(ambientState, algorithmState);
         updateClipping(algorithmState, ambientState);
-        updateSpeedBumpState(algorithmState, ambientState);
+        updateSpeedBumpState(algorithmState, speedBumpIndex);
         updateShelfState(ambientState);
         getNotificationChildrenStates(algorithmState, ambientState);
     }
@@ -136,9 +136,9 @@
     }
 
     private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
-            AmbientState ambientState) {
+            int speedBumpIndex) {
         int childCount = algorithmState.visibleChildren.size();
-        int belowSpeedBump = ambientState.getSpeedBumpIndex();
+        int belowSpeedBump = speedBumpIndex;
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             ExpandableViewState childViewState = child.getViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 8092cb9..3827123f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -55,7 +56,7 @@
     @VisibleForTesting
     final int mExtensionTime;
     private final KeyguardBypassController mBypassController;
-    private final NotificationGroupManager mGroupManager;
+    private final GroupMembershipManager mGroupMembershipManager;
     private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
     private final int mAutoHeadsUpNotificationDecay;
     // TODO (b/162832756): remove visual stability manager when migrating to new pipeline
@@ -101,7 +102,7 @@
     public HeadsUpManagerPhone(@NonNull final Context context,
             StatusBarStateController statusBarStateController,
             KeyguardBypassController bypassController,
-            NotificationGroupManager groupManager,
+            GroupMembershipManager groupMembershipManager,
             ConfigurationController configurationController) {
         super(context);
         Resources resources = mContext.getResources();
@@ -110,7 +111,7 @@
                 R.integer.auto_heads_up_notification_decay);
         statusBarStateController.addCallback(mStatusBarStateListener);
         mBypassController = bypassController;
-        mGroupManager = groupManager;
+        mGroupMembershipManager = groupMembershipManager;
 
         updateResources();
         configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@@ -166,7 +167,7 @@
         } else {
             if (topEntry.isChildInGroup()) {
                 final NotificationEntry groupSummary =
-                        mGroupManager.getGroupSummary(topEntry.getSbn());
+                        mGroupMembershipManager.getGroupSummary(topEntry);
                 if (groupSummary != null) {
                     topEntry = groupSummary;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 858023d..5dfb22f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -27,9 +27,10 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 /**
  * A touch handler of the keyguard which is responsible for launching phone and camera affordances.
@@ -317,7 +318,9 @@
         // We snap back if the current translation is not far enough
         boolean snapBack = false;
         if (mCallback.needsAntiFalsing()) {
-            snapBack = snapBack || mFalsingManager.isFalseTouch();
+            snapBack = snapBack || mFalsingManager.isFalseTouch(
+                    mTargetedView == mRightIcon
+                            ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE);
         }
         snapBack = snapBack || isBelowFalsingThreshold();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index b6a284c..09034c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -40,6 +40,8 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.ContainerView;
+import com.android.keyguard.dagger.KeyguardBouncerScope;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -50,9 +52,12 @@
 
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+
 /**
  * A class which manages the bouncer on the lockscreen.
  */
+@KeyguardBouncerScope
 public class KeyguardBouncer {
 
     private static final String TAG = "KeyguardBouncer";
@@ -95,8 +100,9 @@
     private boolean mIsAnimatingAway;
     private boolean mIsScrimmed;
 
+    @Inject
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
-            LockPatternUtils lockPatternUtils, ViewGroup container,
+            LockPatternUtils lockPatternUtils, @ContainerView ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
             BouncerExpansionCallback expansionCallback,
             KeyguardStateController keyguardStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index dd9c820..3181f52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -31,11 +31,11 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
-import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
-import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -44,8 +44,8 @@
 import java.util.Objects;
 
 /**
- * A helper class dealing with the alert interactions between {@link NotificationGroupManager} and
- * {@link HeadsUpManager}. In particular, this class deals with keeping
+ * A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy}
+ * and {@link HeadsUpManager}. In particular, this class deals with keeping
  * the correct notification in a group alerting based off the group suppression.
  */
 public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
@@ -66,8 +66,8 @@
 
     private HeadsUpManager mHeadsUpManager;
     private final RowContentBindStage mRowContentBindStage;
-    private final NotificationGroupManager mGroupManager =
-            Dependency.get(NotificationGroupManager.class);
+    private final NotificationGroupManagerLegacy mGroupManager =
+            Dependency.get(NotificationGroupManagerLegacy.class);
 
     private NotificationEntryManager mEntryManager;
 
@@ -83,7 +83,7 @@
 
     /** Causes the TransferHelper to register itself as a listener to the appropriate classes. */
     public void bind(NotificationEntryManager entryManager,
-            NotificationGroupManager groupManager) {
+            NotificationGroupManagerLegacy groupManager) {
         if (mEntryManager != null) {
             throw new IllegalStateException("Already bound.");
         }
@@ -95,7 +95,7 @@
         mEntryManager = entryManager;
 
         mEntryManager.addNotificationEntryListener(mNotificationEntryListener);
-        groupManager.addOnGroupChangeListener(mOnGroupChangeListener);
+        groupManager.registerGroupChangeListener(mOnGroupChangeListener);
     }
 
     /**
@@ -128,7 +128,8 @@
         mIsDozing = isDozing;
     }
 
-    private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
+    private final NotificationGroupManagerLegacy.OnGroupChangeListener mOnGroupChangeListener =
+            new NotificationGroupManagerLegacy.OnGroupChangeListener() {
         @Override
         public void onGroupCreated(NotificationGroup group, String groupKey) {
             mGroupAlertEntries.put(groupKey, new GroupAlertEntry(group));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index fc7e548..cd9cc07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,7 @@
 
 import static android.view.View.GONE;
 
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -72,6 +73,7 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeLog;
@@ -84,7 +86,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -105,6 +106,8 @@
 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -118,8 +121,8 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -166,9 +169,6 @@
             mOnHeadsUpChangedListener =
             new MyOnHeadsUpChangedListener();
     private final HeightListener mHeightListener = new HeightListener();
-    private final ZenModeControllerCallback
-            mZenModeControllerCallback =
-            new ZenModeControllerCallback();
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
     private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
     private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
@@ -176,7 +176,6 @@
     private final NotificationPanelView mView;
     private final MetricsLogger mMetricsLogger;
     private final ActivityManager mActivityManager;
-    private final ZenModeController mZenModeController;
     private final ConfigurationController mConfigurationController;
     private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@@ -340,8 +339,6 @@
     private boolean mKeyguardStatusViewAnimating;
     private ValueAnimator mQsSizeChangeAnimator;
 
-    private boolean mShowEmptyShadeView;
-
     private boolean mQsScrimEnabled = true;
     private boolean mQsTouchAboveFalsingThreshold;
     private int mQsFalsingThreshold;
@@ -365,7 +362,8 @@
         setHeadsUpAnimatingAway(false);
         notifyBarPanelExpansionChanged();
     };
-    private NotificationGroupManager mGroupManager;
+    // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager
+    private NotificationGroupManagerLegacy mGroupManager;
     private boolean mShowIconsWhenExpanded;
     private int mIndicationBottomPadding;
     private int mAmbientIndicationBottomPadding;
@@ -503,7 +501,7 @@
             LatencyTracker latencyTracker, PowerManager powerManager,
             AccessibilityManager accessibilityManager, @DisplayId int displayId,
             KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
-            ActivityManager activityManager, ZenModeController zenModeController,
+            ActivityManager activityManager,
             ConfigurationController configurationController,
             FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
@@ -512,20 +510,21 @@
             BiometricUnlockController biometricUnlockController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
-            NotificationIconAreaController notificationIconAreaController,
-            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
+            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
+            NotificationGroupManagerLegacy groupManager,
+            NotificationIconAreaController notificationIconAreaController) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
                 latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
         mView = view;
         mMetricsLogger = metricsLogger;
         mActivityManager = activityManager;
-        mZenModeController = zenModeController;
         mConfigurationController = configurationController;
         mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
         mMediaHierarchyManager = mediaHierarchyManager;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
+        mGroupManager = groupManager;
         mNotificationIconAreaController = notificationIconAreaController;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mView.setWillNotDraw(!DEBUG);
@@ -722,8 +721,6 @@
     }
 
     private void reInflateViews() {
-        updateShowEmptyShadeView();
-
         // Re-inflate the status view group.
         int index = mView.indexOfChild(mKeyguardStatusView);
         mView.removeView(mKeyguardStatusView);
@@ -1268,7 +1265,7 @@
     }
 
     private boolean flingExpandsQs(float vel) {
-        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
+        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
             return false;
         }
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -1278,12 +1275,12 @@
         }
     }
 
-    private boolean isFalseTouch() {
+    private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
             return false;
         }
         if (mFalsingManager.isClassifierEnabled()) {
-            return mFalsingManager.isFalseTouch();
+            return mFalsingManager.isFalseTouch(interactionType);
         }
         return !mQsTouchAboveFalsingThreshold;
     }
@@ -1725,7 +1722,6 @@
         mNotificationStackScrollLayoutController.setScrollingEnabled(
                 mBarState != KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
-        updateEmptyShadeView();
 
         mQsNavbarScrim.setVisibility(
                 mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
@@ -2143,7 +2139,7 @@
         // it in expanded QS state as well so we don't run into troubles when fading the view in/out
         // and expanding/collapsing the whole panel from/to quick settings.
         if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0
-                && mShowEmptyShadeView) {
+                && mNotificationStackScrollLayoutController.isShowingEmptyShadeView()) {
             notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight();
         }
         int maxQsHeight = mQsMaxExpansionHeight;
@@ -2559,17 +2555,6 @@
         return mDozing;
     }
 
-    public void showEmptyShadeView(boolean emptyShadeViewVisible) {
-        mShowEmptyShadeView = emptyShadeViewVisible;
-        updateEmptyShadeView();
-    }
-
-    private void updateEmptyShadeView() {
-        // Hide "No notifications" in QS.
-        mNotificationStackScrollLayoutController.updateEmptyShadeView(
-                mShowEmptyShadeView && !mQsExpanded);
-    }
-
     public void setQsScrimEnabled(boolean qsScrimEnabled) {
         boolean changed = mQsScrimEnabled != qsScrimEnabled;
         mQsScrimEnabled = qsScrimEnabled;
@@ -2871,10 +2856,6 @@
         return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
     }
 
-    private void setGroupManager(NotificationGroupManager groupManager) {
-        mGroupManager = groupManager;
-    }
-
     public boolean hideStatusBarIconsWhenExpanded() {
         if (mLaunchingNotification) {
             return mHideIconsDuringNotificationLaunch;
@@ -3070,31 +3051,26 @@
         if (mKeyguardStatusBar != null) {
             mKeyguardStatusBar.dump(fd, pw, args);
         }
-        if (mKeyguardStatusView != null) {
-            mKeyguardStatusView.dump(fd, pw, args);
-        }
     }
 
     public boolean hasActiveClearableNotifications() {
         return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL);
     }
 
-    private void updateShowEmptyShadeView() {
-        boolean
-                showEmptyShadeView =
-                mBarState != KEYGUARD && !mEntryManager.hasActiveNotifications();
-        showEmptyShadeView(showEmptyShadeView);
-    }
-
     public RemoteInputController.Delegate createRemoteInputDelegate() {
         return mNotificationStackScrollLayoutController.createDelegate();
     }
 
-    void updateNotificationViews(String reason) {
+    /**
+     * Updates the notification views' sections and status bar icons. This is
+     * triggered by the NotificationPresenter whenever there are changes to the underlying
+     * notification data being displayed. In the new notification pipeline, this is handled in
+     * {@link ShadeViewManager}.
+     */
+    public void updateNotificationViews(String reason) {
         mNotificationStackScrollLayoutController.updateSectionBoundaries(reason);
-        mNotificationStackScrollLayoutController.updateSpeedBumpIndex();
         mNotificationStackScrollLayoutController.updateFooter();
-        updateShowEmptyShadeView();
+
         mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList());
     }
 
@@ -3140,17 +3116,10 @@
      */
     public void initDependencies(
             StatusBar statusBar,
-            NotificationGroupManager groupManager,
-            NotificationShelfController notificationShelfController,
-            ScrimController scrimController) {
+            NotificationShelfController notificationShelfController) {
         setStatusBar(statusBar);
-        setGroupManager(mGroupManager);
         mNotificationStackScrollLayoutController.setNotificationPanelController(this);
-        mNotificationStackScrollLayoutController.setStatusBar(statusBar);
-        mNotificationStackScrollLayoutController.setGroupManager(groupManager);
         mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
-        mNotificationStackScrollLayoutController.setScrimController(scrimController);
-        updateShowEmptyShadeView();
         mNotificationShelfController = notificationShelfController;
         updateMaxDisplayedNotifications(true);
     }
@@ -3547,7 +3516,6 @@
     private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
         @Override
         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
-            mNotificationStackScrollLayoutController.setInHeadsUpPinnedMode(inPinnedMode);
             if (inPinnedMode) {
                 mHeadsUpExistenceChangedRunnable.run();
                 updateNotificationTranslucency();
@@ -3606,20 +3574,8 @@
         }
     }
 
-    private class ZenModeControllerCallback implements ZenModeController.Callback {
-        @Override
-        public void onZenChanged(int zen) {
-            updateShowEmptyShadeView();
-        }
-    }
-
     private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
         @Override
-        public void onDensityOrFontScaleChanged() {
-            updateShowEmptyShadeView();
-        }
-
-        @Override
         public void onThemeChanged() {
             final int themeResId = mView.getContext().getThemeResId();
             if (mThemeResId == themeResId) {
@@ -3716,7 +3672,6 @@
         public void onViewAttachedToWindow(View v) {
             FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener);
             mStatusBarStateController.addCallback(mStatusBarStateListener);
-            mZenModeController.addCallback(mZenModeControllerCallback);
             mConfigurationController.addCallback(mConfigurationListener);
             mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
             // Theme might have changed between inflating this view and attaching it to the
@@ -3729,7 +3684,6 @@
         public void onViewDetachedFromWindow(View v) {
             FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener);
             mStatusBarStateController.removeCallback(mStatusBarStateListener);
-            mZenModeController.removeCallback(mZenModeControllerCallback);
             mConfigurationController.removeCallback(mConfigurationListener);
             mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 53cc267..a3d3c2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.InjectionInflationController;
@@ -83,6 +84,7 @@
     private final NotificationShadeWindowView mView;
     private final ShadeController mShadeController;
     private final NotificationShadeDepthController mDepthController;
+    private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
     private GestureDetector mGestureDetector;
     private View mBrightnessMirror;
@@ -130,7 +132,8 @@
             NotificationShadeDepthController depthController,
             NotificationShadeWindowView notificationShadeWindowView,
             NotificationPanelViewController notificationPanelViewController,
-            SuperStatusBarViewFactory statusBarViewFactory) {
+            SuperStatusBarViewFactory statusBarViewFactory,
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
         mInjectionInflationController = injectionInflationController;
         mCoordinator = coordinator;
         mPulseExpansionHandler = pulseExpansionHandler;
@@ -152,6 +155,7 @@
         mNotificationPanelViewController = notificationPanelViewController;
         mDepthController = depthController;
         mStatusBarViewFactory = statusBarViewFactory;
+        mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
@@ -245,7 +249,7 @@
                     }
                 }
                 if (isDown) {
-                    mStackScrollLayout.closeControlsIfOutsideTouch(ev);
+                    mNotificationStackScrollLayoutController.closeControlsIfOutsideTouch(ev);
                 }
                 if (mStatusBarStateController.isDozing()) {
                     mService.mDozeScrimController.extendPulse();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 965368e..6fa99ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+import static com.android.systemui.classifier.Classifier.UNLOCK;
+
 import static java.lang.Float.isNaN;
 
 import android.animation.Animator;
@@ -41,14 +45,15 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -397,7 +402,12 @@
                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
             }
-            fling(vel, expand, isFalseTouch(x, y));
+            @Classifier.InteractionType int interactionType = vel > 0
+                    ? QUICK_SETTINGS : (
+                            mKeyguardStateController.canDismissLockScreen()
+                                    ? UNLOCK : BOUNCER_UNLOCK);
+
+            fling(vel, expand, isFalseTouch(x, y, interactionType));
             onTrackingStopped(expand);
             mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
             if (mUpdateFlingOnLayout) {
@@ -492,7 +502,11 @@
             return true;
         }
 
-        if (isFalseTouch(x, y)) {
+        @Classifier.InteractionType int interactionType = vel > 0
+                ? QUICK_SETTINGS : (
+                        mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
+
+        if (isFalseTouch(x, y, interactionType)) {
             return true;
         }
         if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -511,12 +525,13 @@
      * @param y the final y-coordinate when the finger was lifted
      * @return whether this motion should be regarded as a false touch
      */
-    private boolean isFalseTouch(float x, float y) {
+    private boolean isFalseTouch(float x, float y,
+            @Classifier.InteractionType int interactionType) {
         if (!mStatusBar.isFalsingThresholdNeeded()) {
             return false;
         }
         if (mFalsingManager.isClassifierEnabled()) {
-            return mFalsingManager.isFalseTouch();
+            return mFalsingManager.isFalseTouch(interactionType);
         }
         if (!mTouchAboveFalsingThreshold) {
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 11d0583..e95cf28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -141,6 +141,8 @@
     private ScrimView mScrimBehind;
     private ScrimView mScrimForBubble;
 
+    private Runnable mScrimBehindChangeRunnable;
+
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DozeParameters mDozeParameters;
@@ -241,6 +243,11 @@
         mScrimInFront = scrimInFront;
         mScrimForBubble = scrimForBubble;
 
+        if (mScrimBehindChangeRunnable != null) {
+            mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable);
+            mScrimBehindChangeRunnable = null;
+        }
+
         final ScrimState[] states = ScrimState.values();
         for (int i = 0; i < states.length; i++) {
             states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters,
@@ -934,7 +941,13 @@
     }
 
     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
-        mScrimBehind.setChangeRunnable(changeRunnable);
+        // TODO: remove this. This is necessary because of an order-of-operations limitation.
+        // The fix is to move more of these class into @StatusBarScope
+        if (mScrimBehind == null) {
+            mScrimBehindChangeRunnable = changeRunnable;
+        } else {
+            mScrimBehind.setChangeRunnable(changeRunnable);
+        }
     }
 
     public void setCurrentUser(int currentUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 115d164..31c1a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -177,7 +177,6 @@
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
@@ -231,6 +230,7 @@
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.volume.VolumeComponent;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -699,7 +699,6 @@
             SysuiStatusBarStateController statusBarStateController,
             VibratorHelper vibratorHelper,
             BubbleController bubbleController,
-            NotificationGroupManager groupManager,
             VisualStabilityManager visualStabilityManager,
             DeviceProvisionedController deviceProvisionedController,
             NavigationBarController navigationBarController,
@@ -780,7 +779,6 @@
         mStatusBarStateController = statusBarStateController;
         mVibratorHelper = vibratorHelper;
         mBubbleController = bubbleController;
-        mGroupManager = groupManager;
         mVisualStabilityManager = visualStabilityManager;
         mDeviceProvisionedController = deviceProvisionedController;
         mNavigationBarController = navigationBarController;
@@ -1159,9 +1157,7 @@
 
         mNotificationPanelViewController.initDependencies(
                 this,
-                mGroupManager,
-                mNotificationShelfController,
-                mScrimController);
+                mNotificationShelfController);
 
         BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
         mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
@@ -1507,9 +1503,8 @@
         mStatusBarKeyguardViewManager.registerStatusBar(
                 /* statusBar= */ this, getBouncerContainer(),
                 mNotificationPanelViewController, mBiometricUnlockController,
-                mDismissCallbackRegistry,
                 mNotificationShadeWindowView.findViewById(R.id.lock_icon_container),
-                mStackScroller, mKeyguardBypassController, mFalsingManager);
+                mStackScroller, mKeyguardBypassController);
         mKeyguardIndicationController
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
@@ -4085,8 +4080,6 @@
     // all notifications
     protected NotificationStackScrollLayout mStackScroller;
 
-    private final NotificationGroupManager mGroupManager;
-
     // handling reordering
     private final VisualStabilityManager mVisualStabilityManager;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 777bf3f7..b56993b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -44,15 +44,13 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.systemui.DejankUtils;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dock.DockManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
@@ -106,6 +104,7 @@
     private final NavigationModeController mNavigationModeController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
+    private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
         public void onFullyShown() {
@@ -216,7 +215,8 @@
             NotificationShadeWindowController notificationShadeWindowController,
             KeyguardStateController keyguardStateController,
             Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
-            NotificationMediaManager notificationMediaManager) {
+            NotificationMediaManager notificationMediaManager,
+            KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -229,6 +229,7 @@
         mStatusBarStateController = sysuiStatusBarStateController;
         mDockManager = dockManager;
         mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
+        mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
     }
 
     @Override
@@ -236,9 +237,8 @@
             ViewGroup container,
             NotificationPanelViewController notificationPanelViewController,
             BiometricUnlockController biometricUnlockController,
-            DismissCallbackRegistry dismissCallbackRegistry,
             ViewGroup lockIconContainer, View notificationContainer,
-            KeyguardBypassController bypassController, FalsingManager falsingManager) {
+            KeyguardBypassController bypassController) {
         mStatusBar = statusBar;
         mContainer = container;
         mLockIconContainer = lockIconContainer;
@@ -246,9 +246,9 @@
             mLastLockVisible = mLockIconContainer.getVisibility() == View.VISIBLE;
         }
         mBiometricUnlockController = biometricUnlockController;
-        mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
-                mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
-                mExpansionCallback, mKeyguardStateController, falsingManager, bypassController);
+        mBouncer = mKeyguardBouncerComponentFactory
+                .build(container, mExpansionCallback)
+                .createKeyguardBouncer();
         mNotificationPanelViewController = notificationPanelViewController;
         notificationPanelViewController.addExpansionListener(this);
         mBypassController = bypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index de11c90..737cdeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -66,10 +66,10 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -96,7 +96,6 @@
 
     private final NotificationEntryManager mEntryManager;
     private final NotifPipeline mNotifPipeline;
-    private final NotifCollection mNotifCollection;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final ActivityStarter mActivityStarter;
     private final NotificationClickNotifier mClickNotifier;
@@ -107,7 +106,7 @@
     private final BubbleController mBubbleController;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final NotificationRemoteInputManager mRemoteInputManager;
-    private final NotificationGroupManager mGroupManager;
+    private final GroupMembershipManager mGroupMembershipManager;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final ShadeController mShadeController;
     private final KeyguardStateController mKeyguardStateController;
@@ -135,7 +134,6 @@
             Executor uiBgExecutor,
             NotificationEntryManager entryManager,
             NotifPipeline notifPipeline,
-            NotifCollection notifCollection,
             HeadsUpManagerPhone headsUpManager,
             ActivityStarter activityStarter,
             NotificationClickNotifier clickNotifier,
@@ -146,7 +144,7 @@
             BubbleController bubbleController,
             Lazy<AssistManager> assistManagerLazy,
             NotificationRemoteInputManager remoteInputManager,
-            NotificationGroupManager groupManager,
+            GroupMembershipManager groupMembershipManager,
             NotificationLockscreenUserManager lockscreenUserManager,
             ShadeController shadeController,
             KeyguardStateController keyguardStateController,
@@ -170,7 +168,6 @@
         mUiBgExecutor = uiBgExecutor;
         mEntryManager = entryManager;
         mNotifPipeline = notifPipeline;
-        mNotifCollection = notifCollection;
         mHeadsUpManager = headsUpManager;
         mActivityStarter = activityStarter;
         mClickNotifier = clickNotifier;
@@ -181,7 +178,7 @@
         mBubbleController = bubbleController;
         mAssistManagerLazy = assistManagerLazy;
         mRemoteInputManager = remoteInputManager;
-        mGroupManager = groupManager;
+        mGroupMembershipManager = groupMembershipManager;
         mLockscreenUserManager = lockscreenUserManager;
         mShadeController = shadeController;
         mKeyguardStateController = keyguardStateController;
@@ -228,8 +225,9 @@
     public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
         mLogger.logStartingActivityFromClick(sbn.getKey());
 
+        final NotificationEntry entry = row.getEntry();
         RemoteInputController controller = mRemoteInputManager.getController();
-        if (controller.isRemoteInputActive(row.getEntry())
+        if (controller.isRemoteInputActive(entry)
                 && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
             // We have an active remote input typed and the user clicked on the notification.
             // this was probably unintentional, so we're closing the edit text instead.
@@ -240,7 +238,7 @@
         final PendingIntent intent = notification.contentIntent != null
                 ? notification.contentIntent
                 : notification.fullScreenIntent;
-        final boolean isBubble = row.getEntry().isBubble();
+        final boolean isBubble = entry.isBubble();
 
         // This code path is now executed for notification without a contentIntent.
         // The only valid case is Bubble notifications. Guard against other cases
@@ -260,7 +258,7 @@
                 mLockscreenUserManager.getCurrentUserId());
         ActivityStarter.OnDismissAction postKeyguardAction =
                 () -> handleNotificationClickAfterKeyguardDismissed(
-                        sbn, row, controller, intent,
+                        entry, row, controller, intent,
                         isActivityIntent, wasOccluded, showOverLockscreen);
         if (showOverLockscreen) {
             mIsCollapsingToShowActivityOverLockscreen = true;
@@ -272,28 +270,21 @@
     }
 
     private boolean handleNotificationClickAfterKeyguardDismissed(
-            StatusBarNotification sbn,
+            NotificationEntry entry,
             ExpandableNotificationRow row,
             RemoteInputController controller,
             PendingIntent intent,
             boolean isActivityIntent,
             boolean wasOccluded,
             boolean showOverLockscreen) {
-        mLogger.logHandleClickAfterKeyguardDismissed(sbn.getKey());
+        mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey());
 
         // TODO: Some of this code may be able to move to NotificationEntryManager.
         removeHUN(row);
-        NotificationEntry parentToCancel = null;
-        if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
-            NotificationEntry summarySbn = mGroupManager.getLogicalGroupSummary(sbn);
-            if (shouldAutoCancel(summarySbn.getSbn())) {
-                parentToCancel = summarySbn;
-            }
-        }
-        final NotificationEntry parentToCancelFinal = parentToCancel;
+
         final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
-                sbn, row, controller, intent,
-                isActivityIntent, wasOccluded, parentToCancelFinal);
+                entry, row, controller, intent,
+                isActivityIntent, wasOccluded);
 
         if (showOverLockscreen) {
             mShadeController.addPostCollapseAction(runnable);
@@ -309,16 +300,15 @@
     }
 
     private void handleNotificationClickAfterPanelCollapsed(
-            StatusBarNotification sbn,
+            NotificationEntry entry,
             ExpandableNotificationRow row,
             RemoteInputController controller,
             PendingIntent intent,
             boolean isActivityIntent,
-            boolean wasOccluded,
-            NotificationEntry parentToCancelFinal) {
-        mLogger.logHandleClickAfterPanelCollapsed(sbn.getKey());
+            boolean wasOccluded) {
+        String notificationKey = entry.getKey();
+        mLogger.logHandleClickAfterPanelCollapsed(notificationKey);
 
-        String notificationKey = sbn.getKey();
         try {
             // The intent we are sending is for the application, which
             // won't have permission to immediately start an activity after
@@ -346,7 +336,6 @@
             }
         }
         Intent fillInIntent = null;
-        NotificationEntry entry = row.getEntry();
         CharSequence remoteInputText = null;
         if (!TextUtils.isEmpty(entry.remoteInputText)) {
             remoteInputText = entry.remoteInputText;
@@ -376,22 +365,23 @@
                 NotificationLogger.getNotificationLocation(entry);
         final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
                 rank, count, true, location);
+
+        // NMS will officially remove notification if the notification has FLAG_AUTO_CANCEL:
         mClickNotifier.onNotificationClick(notificationKey, nv);
 
-        if (!canBubble) {
-            if (parentToCancelFinal != null) {
-                // TODO: (b/145659174) remove - this cancels the parent if the notification clicked
-                // on will auto-cancel and is the only child in the group. This won't be
-                // necessary in the new pipeline due to group pruning in ShadeListBuilder.
-                removeNotification(parentToCancelFinal);
-            }
-            if (shouldAutoCancel(sbn)
+        // TODO (b/162832756): delete these notification removals when migrating to the new
+        //  pipeline; this is taken care of in {@link NotifCollection#tryRemoveNotification}
+        //  which cancels lifetime extenders if the notification was dismissed by the user (ie:
+        //  clicked or manually dismissed)
+        if (!canBubble && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            if (shouldAutoCancel(entry.getSbn())
                     || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
                     notificationKey)) {
-                // Automatically remove all notifications that we may have kept around longer
+                // manually call notification removal in order to cancel any lifetime extenders
                 removeNotification(row.getEntry());
             }
         }
+
         mIsCollapsingToShowActivityOverLockscreen = false;
     }
 
@@ -605,7 +595,6 @@
         private final Executor mUiBgExecutor;
         private final NotificationEntryManager mEntryManager;
         private final NotifPipeline mNotifPipeline;
-        private final NotifCollection mNotifCollection;
         private final HeadsUpManagerPhone mHeadsUpManager;
         private final ActivityStarter mActivityStarter;
         private final NotificationClickNotifier mClickNotifier;
@@ -616,7 +605,7 @@
         private final BubbleController mBubbleController;
         private final Lazy<AssistManager> mAssistManagerLazy;
         private final NotificationRemoteInputManager mRemoteInputManager;
-        private final NotificationGroupManager mGroupManager;
+        private final GroupMembershipManager mGroupMembershipManager;
         private final NotificationLockscreenUserManager mLockscreenUserManager;
         private final ShadeController mShadeController;
         private final KeyguardStateController mKeyguardStateController;
@@ -643,7 +632,6 @@
                 @UiBackground Executor uiBgExecutor,
                 NotificationEntryManager entryManager,
                 NotifPipeline notifPipeline,
-                NotifCollection notifCollection,
                 HeadsUpManagerPhone headsUpManager,
                 ActivityStarter activityStarter,
                 NotificationClickNotifier clickNotifier,
@@ -654,7 +642,7 @@
                 BubbleController bubbleController,
                 Lazy<AssistManager> assistManagerLazy,
                 NotificationRemoteInputManager remoteInputManager,
-                NotificationGroupManager groupManager,
+                GroupMembershipManager groupMembershipManager,
                 NotificationLockscreenUserManager lockscreenUserManager,
                 ShadeController shadeController,
                 KeyguardStateController keyguardStateController,
@@ -674,7 +662,6 @@
             mUiBgExecutor = uiBgExecutor;
             mEntryManager = entryManager;
             mNotifPipeline = notifPipeline;
-            mNotifCollection = notifCollection;
             mHeadsUpManager = headsUpManager;
             mActivityStarter = activityStarter;
             mClickNotifier = clickNotifier;
@@ -685,7 +672,7 @@
             mBubbleController = bubbleController;
             mAssistManagerLazy = assistManagerLazy;
             mRemoteInputManager = remoteInputManager;
-            mGroupManager = groupManager;
+            mGroupMembershipManager = groupMembershipManager;
             mLockscreenUserManager = lockscreenUserManager;
             mShadeController = shadeController;
             mKeyguardStateController = keyguardStateController;
@@ -731,7 +718,6 @@
                     mUiBgExecutor,
                     mEntryManager,
                     mNotifPipeline,
-                    mNotifCollection,
                     mHeadsUpManager,
                     mActivityStarter,
                     mClickNotifier,
@@ -742,7 +728,7 @@
                     mBubbleController,
                     mAssistManagerLazy,
                     mRemoteInputManager,
-                    mGroupManager,
+                    mGroupMembershipManager,
                     mLockscreenUserManager,
                     mShadeController,
                     mKeyguardStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 8a89429..36519ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -65,7 +66,7 @@
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final ShadeController mShadeController;
     private final ActivityIntentHelper mActivityIntentHelper;
-    private final NotificationGroupManager mGroupManager;
+    private final GroupExpansionManager mGroupExpansionManager;
     private View mPendingWorkRemoteInputView;
     private View mPendingRemoteInputView;
     private KeyguardManager mKeyguardManager;
@@ -78,12 +79,15 @@
     /**
      */
     @Inject
-    public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager,
+    public StatusBarRemoteInputCallback(
+            Context context,
+            GroupExpansionManager groupExpansionManager,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             KeyguardStateController keyguardStateController,
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            ActivityStarter activityStarter, ShadeController shadeController,
+            ActivityStarter activityStarter,
+            ShadeController shadeController,
             CommandQueue commandQueue,
             ActionClickLogger clickLogger) {
         mContext = context;
@@ -101,7 +105,7 @@
         mCommandQueue.addCallback(this);
         mActionClickLogger = clickLogger;
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
-        mGroupManager = groupManager;
+        mGroupExpansionManager = groupExpansionManager;
     }
 
     @Override
@@ -182,7 +186,7 @@
         } else {
             if (row.isChildInGroup() && !row.areChildrenExpanded()) {
                 // The group isn't expanded, let's make sure it's visible!
-                mGroupManager.toggleGroupExpansion(row.getEntry().getSbn());
+                mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
             }
             row.setUserExpanded(true);
             row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 72067d3..b7f8314 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -46,7 +46,6 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -80,7 +79,6 @@
 import com.android.systemui.statusbar.phone.LightsOutNotifController;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -100,6 +98,7 @@
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.volume.VolumeComponent;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -158,7 +157,6 @@
             SysuiStatusBarStateController statusBarStateController,
             VibratorHelper vibratorHelper,
             BubbleController bubbleController,
-            NotificationGroupManager groupManager,
             VisualStabilityManager visualStabilityManager,
             DeviceProvisionedController deviceProvisionedController,
             NavigationBarController navigationBarController,
@@ -238,7 +236,6 @@
                 statusBarStateController,
                 vibratorHelper,
                 bubbleController,
-                groupManager,
                 visualStabilityManager,
                 deviceProvisionedController,
                 navigationBarController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
index 9b4e165..485b1b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -15,8 +15,6 @@
 package com.android.systemui.statusbar.policy;
 
 import android.app.ActivityManager;
-import android.content.ContentResolver;
-import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
@@ -30,6 +28,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.ArrayList;
 
@@ -43,8 +43,8 @@
 
     protected static final String TAG = DeviceProvisionedControllerImpl.class.getSimpleName();
     protected final ArrayList<DeviceProvisionedListener> mListeners = new ArrayList<>();
-    private final ContentResolver mContentResolver;
-    private final Context mContext;
+    private final GlobalSettings mGlobalSettings;
+    private final SecureSettings mSecureSettings;
     private final Uri mDeviceProvisionedUri;
     private final Uri mUserSetupUri;
     protected final ContentObserver mSettingsObserver;
@@ -52,13 +52,14 @@
     /**
      */
     @Inject
-    public DeviceProvisionedControllerImpl(Context context, @Main Handler mainHandler,
-            BroadcastDispatcher broadcastDispatcher) {
+    public DeviceProvisionedControllerImpl(@Main Handler mainHandler,
+            BroadcastDispatcher broadcastDispatcher, GlobalSettings globalSettings,
+            SecureSettings secureSettings) {
         super(broadcastDispatcher);
-        mContext = context;
-        mContentResolver = context.getContentResolver();
-        mDeviceProvisionedUri = Global.getUriFor(Global.DEVICE_PROVISIONED);
-        mUserSetupUri = Secure.getUriFor(Secure.USER_SETUP_COMPLETE);
+        mGlobalSettings = globalSettings;
+        mSecureSettings = secureSettings;
+        mDeviceProvisionedUri = mGlobalSettings.getUriFor(Global.DEVICE_PROVISIONED);
+        mUserSetupUri = mSecureSettings.getUriFor(Secure.USER_SETUP_COMPLETE);
         mSettingsObserver = new ContentObserver(mainHandler) {
             @Override
             public void onChange(boolean selfChange, Uri uri, int flags) {
@@ -74,13 +75,12 @@
 
     @Override
     public boolean isDeviceProvisioned() {
-        return Global.getInt(mContentResolver, Global.DEVICE_PROVISIONED, 0) != 0;
+        return mGlobalSettings.getInt(Global.DEVICE_PROVISIONED, 0) != 0;
     }
 
     @Override
     public boolean isUserSetup(int currentUser) {
-        return Secure.getIntForUser(mContentResolver, Secure.USER_SETUP_COMPLETE, 0, currentUser)
-                != 0;
+        return mSecureSettings.getIntForUser(Secure.USER_SETUP_COMPLETE, 0, currentUser) != 0;
     }
 
     @Override
@@ -107,24 +107,24 @@
     }
 
     protected void startListening(int user) {
-        mContentResolver.registerContentObserver(mDeviceProvisionedUri, true,
+        mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true,
                 mSettingsObserver, 0);
-        mContentResolver.registerContentObserver(mUserSetupUri, true,
+        mSecureSettings.registerContentObserverForUser(mUserSetupUri, true,
                 mSettingsObserver, user);
         startTracking();
     }
 
     protected void stopListening() {
         stopTracking();
-        mContentResolver.unregisterContentObserver(mSettingsObserver);
+        mGlobalSettings.unregisterContentObserver(mSettingsObserver);
     }
 
     @Override
     public void onUserSwitched(int newUserId) {
-        mContentResolver.unregisterContentObserver(mSettingsObserver);
-        mContentResolver.registerContentObserver(mDeviceProvisionedUri, true,
+        mGlobalSettings.unregisterContentObserver(mSettingsObserver);
+        mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true,
                 mSettingsObserver, 0);
-        mContentResolver.registerContentObserver(mUserSetupUri, true,
+        mSecureSettings.registerContentObserverForUser(mUserSetupUri, true,
                 mSettingsObserver, newUserId);
         notifyUserChanged();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index cf83603..eb2d9bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -418,6 +418,10 @@
         return (mServiceState != null && mServiceState.isEmergencyOnly());
     }
 
+    public boolean isInService() {
+        return Utils.isInService(mServiceState);
+    }
+
     private boolean isRoaming() {
         // During a carrier change, roaming indications need to be supressed.
         if (isCarrierNetworkChangeActive()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index b790c92..8722fec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -35,6 +35,7 @@
     DataUsageController getMobileDataController();
     DataSaverController getDataSaverController();
     String getMobileDataNetworkName();
+    boolean isMobileDataNetworkInService();
     int getNumberSubscriptions();
 
     boolean hasVoiceCallingFeature();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 2253ce7..62b922e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -460,6 +460,12 @@
     }
 
     @Override
+    public boolean isMobileDataNetworkInService() {
+        MobileSignalController controller = getDataController();
+        return controller != null && controller.isInService();
+    }
+
+    @Override
     public int getNumberSubscriptions() {
         return mMobileSignalControllers.size();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index f9ac760..17fcb1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -206,7 +206,7 @@
             @Override
             protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
                 final SparseArray<Bitmap> bitmaps = params[0];
-                List<UserInfo> infos = mUserManager.getUsers(true);
+                List<UserInfo> infos = mUserManager.getAliveUsers();
                 if (infos == null) {
                     return null;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
new file mode 100644
index 0000000..914105f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.dagger;
+
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SensorPrivacyController;
+import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+
+/** Dagger Module for code in the statusbar.policy package. */
+@Module
+public interface StatusBarPolicyModule {
+    /** */
+    @Binds
+    BluetoothController provideBluetoothController(BluetoothControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    CastController provideCastController(CastControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    ExtensionController provideExtensionController(ExtensionControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    FlashlightController provideFlashlightController(FlashlightControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    KeyguardStateController provideKeyguardMonitor(KeyguardStateControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    HotspotController provideHotspotController(HotspotControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    LocationController provideLocationController(LocationControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    NetworkController provideNetworkController(NetworkControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    NextAlarmController provideNextAlarmController(NextAlarmControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    RotationLockController provideRotationLockController(RotationLockControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    SecurityController provideSecurityController(SecurityControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    SensorPrivacyController provideSensorPrivacyControllerImpl(
+            SensorPrivacyControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    UserInfoController provideUserInfoContrller(UserInfoControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl);
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
index d940a6b..eb23b9d 100644
--- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
+++ b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
@@ -16,6 +16,8 @@
 
 syntax = "proto2";
 
+import "frameworks/base/libs/WindowManager/Shell/proto/wm_shell_trace.proto";
+
 package com.android.systemui.tracing;
 
 option java_multiple_files = true;
@@ -23,6 +25,7 @@
 message SystemUiTraceProto {
 
     optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1;
+    optional com.android.wm.shell.WmShellTraceProto wm_shell = 2;
 }
 
 message EdgeBackGestureHandlerProto {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java
index 114c30e..faf7b84 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.tuner.dagger;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+import dagger.Binds;
+import dagger.Module;
 
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
+/** Dagger Module for code in the tuner package. */
+@Module
+public interface TunerModule {
+    /** */
+    @Binds
+    TunerService provideTunerService(TunerServiceImpl controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
index 3577bc0..bef05eb 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
@@ -17,7 +17,6 @@
 package com.android.systemui.tv;
 
 import com.android.systemui.dagger.DefaultComponentBinder;
-import com.android.systemui.dagger.DependencyBinder;
 import com.android.systemui.dagger.DependencyProvider;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.SysUISingleton;
@@ -33,7 +32,6 @@
 @Subcomponent(modules = {
         DefaultComponentBinder.class,
         DependencyProvider.class,
-        DependencyBinder.class,
         SystemUIBinder.class,
         SystemUIModule.class,
         TvSystemUIModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 9a44bf1..22fa010 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -17,12 +17,11 @@
 package com.android.systemui.tv;
 
 import com.android.systemui.dagger.GlobalRootComponent;
-import com.android.systemui.pip.tv.dagger.PipModule;
 
 import dagger.Binds;
 import dagger.Module;
 
-@Module(includes = {PipModule.class})
+@Module()
 interface TvSystemUIBinder {
     @Binds
     GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index e7c10f1..c5bb9c1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -47,11 +47,11 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.ShadeControllerImpl;
@@ -62,7 +62,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.wmshell.WMShellModule;
+import com.android.systemui.wmshell.TvWMShellModule;
 
 import javax.inject.Named;
 
@@ -76,7 +76,7 @@
  */
 @Module(includes = {
             QSModule.class,
-            WMShellModule.class
+            TvWMShellModule.class,
         },
         subcomponents = {
         })
@@ -136,7 +136,7 @@
             Context context,
             StatusBarStateController statusBarStateController,
             KeyguardBypassController bypassController,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             ConfigurationController configurationController) {
         return new HeadsUpManagerPhone(context, statusBarStateController, bypassController,
                 groupManager, configurationController);
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
new file mode 100644
index 0000000..5946af3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.util.concurrency;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.concurrent.Executor;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module for classes found within the concurrent package.
+ */
+@Module
+public abstract class GlobalConcurrencyModule {
+
+    /**
+     * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
+     */
+    @Binds
+    public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
+
+     /** Main Looper */
+    @Provides
+    @Main
+    public static  Looper provideMainLooper() {
+        return Looper.getMainLooper();
+    }
+
+    /**
+     * Main Handler.
+     *
+     * Prefer the Main Executor when possible.
+     */
+    @Provides
+    @Main
+    public static Handler provideMainHandler(@Main Looper mainLooper) {
+        return new Handler(mainLooper);
+    }
+
+    /**
+     * Provide a Main-Thread Executor.
+     */
+    @Provides
+    @Main
+    public static Executor provideMainExecutor(Context context) {
+        return context.getMainExecutor();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
rename to packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 628c808..b9b20c7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.util.concurrency;
 
-import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -31,7 +30,6 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 
@@ -39,7 +37,7 @@
  * Dagger Module for classes found within the concurrent package.
  */
 @Module
-public abstract class ConcurrencyModule {
+public abstract class SysUIConcurrencyModule {
     /** Background Looper */
     @Provides
     @SysUISingleton
@@ -62,13 +60,6 @@
         return thread.getLooper();
     }
 
-    /** Main Looper */
-    @Provides
-    @Main
-    public static  Looper provideMainLooper() {
-        return Looper.getMainLooper();
-    }
-
     /**
      * Background Handler.
      *
@@ -81,17 +72,6 @@
     }
 
     /**
-     * Main Handler.
-     *
-     * Prefer the Main Executor when possible.
-     */
-    @Provides
-    @Main
-    public static Handler provideMainHandler(@Main Looper mainLooper) {
-        return new Handler(mainLooper);
-    }
-
-    /**
      * Provide a Background-Thread Executor by default.
      */
     @Provides
@@ -121,15 +101,6 @@
     }
 
     /**
-     * Provide a Main-Thread Executor.
-     */
-    @Provides
-    @Main
-    public static Executor provideMainExecutor(Context context) {
-        return context.getMainExecutor();
-    }
-
-    /**
      * Provide a Background-Thread Executor by default.
      */
     @Provides
@@ -199,10 +170,4 @@
     public static Executor provideUiBackgroundExecutor() {
         return Executors.newSingleThreadExecutor();
     }
-
-    /**
-     * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
-     */
-    @Binds
-    public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java
copy to packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index c8b6982..cdfa145 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,21 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.util.dagger;
 
-import com.android.systemui.pip.BasePipManager;
-import com.android.systemui.pip.phone.PipManager;
+import com.android.systemui.util.RingerModeTracker;
+import com.android.systemui.util.RingerModeTrackerImpl;
 
 import dagger.Binds;
 import dagger.Module;
 
-/**
- * Dagger Module for Phone PIP.
- */
+/** Dagger Module for code in the util package. */
 @Module
-public abstract class PipModule {
-
-    /** Binds PipManager as the default BasePipManager. */
+public interface UtilModule {
+    /** */
     @Binds
-    public abstract BasePipManager providePipManager(PipManager pipManager);
+    RingerModeTracker provideRingerModeTracker(RingerModeTrackerImpl ringerModeTrackerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
index 5c37f79..5aaf7f6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -67,7 +67,35 @@
      * Implicitly calls {@link #getUriFor(String)} on the passed in name.
      */
     default void registerContentObserver(String name, ContentObserver settingsObserver) {
-        registerContentObserverForUser(name, settingsObserver, getUserId());
+        registerContentObserver(getUriFor(name), settingsObserver);
+    }
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+     */
+    default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
+        registerContentObserverForUser(uri, settingsObserver, getUserId());
+    }
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+     *
+     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+     */
+    default void registerContentObserver(String name, boolean notifyForDescendents,
+            ContentObserver settingsObserver) {
+        registerContentObserver(getUriFor(name), notifyForDescendents, settingsObserver);
+    }
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+     */
+    default void registerContentObserver(Uri uri, boolean notifyForDescendents,
+            ContentObserver settingsObserver) {
+        registerContentObserverForUser(uri, notifyForDescendents, settingsObserver, getUserId());
     }
 
     /**
@@ -78,8 +106,42 @@
      */
     default void registerContentObserverForUser(
             String name, ContentObserver settingsObserver, int userHandle) {
+        registerContentObserverForUser(
+                getUriFor(name), settingsObserver, userHandle);
+    }
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+     */
+    default void registerContentObserverForUser(
+            Uri uri, ContentObserver settingsObserver, int userHandle) {
+        registerContentObserverForUser(
+                uri, false, settingsObserver, userHandle);
+    }
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+     *
+     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+     */
+    default void registerContentObserverForUser(
+            String name, boolean notifyForDescendents, ContentObserver settingsObserver,
+            int userHandle) {
+        registerContentObserverForUser(
+                getUriFor(name), notifyForDescendents, settingsObserver, userHandle);
+    }
+
+    /**
+     * Convenience wrapper around
+     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+     */
+    default void registerContentObserverForUser(
+            Uri uri, boolean notifyForDescendents, ContentObserver settingsObserver,
+            int userHandle) {
         getContentResolver().registerContentObserver(
-                getUriFor(name), false, settingsObserver, userHandle);
+                uri, notifyForDescendents, settingsObserver, userHandle);
     }
 
     /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 51ad30e..78f83d3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -49,6 +49,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
+import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -71,6 +72,7 @@
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.view.ViewStub;
+import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
@@ -109,7 +111,8 @@
  * Methods ending in "H" must be called on the (ui) handler.
  */
 public class VolumeDialogImpl implements VolumeDialog,
-        ConfigurationController.ConfigurationListener {
+        ConfigurationController.ConfigurationListener,
+        ViewTreeObserver.OnComputeInternalInsetsListener {
     private static final String TAG = Util.logTag(VolumeDialogImpl.class);
 
     private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
@@ -126,6 +129,7 @@
     private final H mHandler = new H();
     private final VolumeDialogController mController;
     private final DeviceProvisionedController mDeviceProvisionedController;
+    private final Region mTouchableRegion = new Region();
 
     private Window mWindow;
     private CustomDialog mDialog;
@@ -204,6 +208,33 @@
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
+    @Override
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo internalInsetsInfo) {
+        // Set touchable region insets on the root dialog view. This tells WindowManager that
+        // touches outside of this region should not be delivered to the volume window, and instead
+        // go to the window below. This is the only way to do this - returning false in
+        // onDispatchTouchEvent results in the event being ignored entirely, rather than passed to
+        // the next window.
+        internalInsetsInfo.setTouchableInsets(
+                ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+
+        mTouchableRegion.setEmpty();
+
+        // Set the touchable region to the union of all child view bounds. We don't use touches on
+        // the volume dialog container itself, so this is fine.
+        for (int i = 0; i < mDialogView.getChildCount(); i++) {
+            final View view = mDialogView.getChildAt(i);
+            mTouchableRegion.op(
+                    view.getLeft(),
+                    view.getTop(),
+                    view.getRight(),
+                    view.getBottom(),
+                    Region.Op.UNION);
+        }
+
+        internalInsetsInfo.touchableRegion.set(mTouchableRegion);
+    }
+
     private void initDialog() {
         mDialog = new CustomDialog(mContext);
 
@@ -235,6 +266,7 @@
         mDialogView.setAlpha(0);
         mDialog.setCanceledOnTouchOutside(true);
         mDialog.setOnShowListener(dialog -> {
+            mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
             if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f);
             mDialogView.setAlpha(0);
             mDialogView.animate()
@@ -253,6 +285,11 @@
                     .start();
         });
 
+        mDialog.setOnDismissListener(dialogInterface ->
+                mDialogView
+                        .getViewTreeObserver()
+                        .removeOnComputeInternalInsetsListener(VolumeDialogImpl.this));
+
         mDialogView.setOnHoverListener((v, event) -> {
             int action = event.getActionMasked();
             mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
@@ -1369,6 +1406,11 @@
             super(context, R.style.volume_dialog_theme);
         }
 
+        /**
+         * NOTE: This will only be called for touches within the touchable region of the volume
+         * dialog, as returned by {@link #onComputeInternalInsets}. Other touches, even if they are
+         * within the bounds of the volume dialog, will fall through to the window below.
+         */
         @Override
         public boolean dispatchTouchEvent(MotionEvent ev) {
             rescheduleTimeoutH();
@@ -1387,6 +1429,12 @@
             mHandler.sendEmptyMessage(H.RECHECK_ALL);
         }
 
+        /**
+         * NOTE: This will be called with ACTION_OUTSIDE MotionEvents for touches that occur outside
+         * of the touchable region of the volume dialog (as returned by
+         * {@link #onComputeInternalInsets}) even if those touches occurred within the bounds of the
+         * volume dialog.
+         */
         @Override
         public boolean onTouchEvent(MotionEvent event) {
             if (mShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
similarity index 60%
rename from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java
rename to packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index c8b6982..1ef4c16 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,21 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.volume.dagger;
 
-import com.android.systemui.pip.BasePipManager;
-import com.android.systemui.pip.phone.PipManager;
+import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
 
 import dagger.Binds;
 import dagger.Module;
 
-/**
- * Dagger Module for Phone PIP.
- */
-@Module
-public abstract class PipModule {
 
-    /** Binds PipManager as the default BasePipManager. */
+/** Dagger Module for code in the volume package. */
+@Module
+public interface VolumeModule {
+    /** */
     @Binds
-    public abstract BasePipManager providePipManager(PipManager pipManager);
+    VolumeComponent provideVolumeComponent(VolumeDialogComponent volumeDialogComponent);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
new file mode 100644
index 0000000..0869cf7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 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.wmshell;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.IWindowManager;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.pip.Pip;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
+import com.android.systemui.pip.tv.PipController;
+import com.android.systemui.pip.tv.PipNotification;
+import com.android.systemui.pip.tv.dagger.TvPipComponent;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+
+import java.util.Optional;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies from {@link com.android.wm.shell} which could be customized among different
+ * branches of SystemUI.
+ */
+// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
+@Module(includes = WMShellBaseModule.class, subcomponents = {TvPipComponent.class})
+public class TvWMShellModule {
+    @SysUISingleton
+    @Provides
+    static DisplayImeController provideDisplayImeController(IWindowManager wmService,
+            DisplayController displayController, @Main Handler mainHandler,
+            TransactionPool transactionPool) {
+        return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
+    }
+
+    @SysUISingleton
+    @Provides
+    static Pip providePipController(Context context,
+            BroadcastDispatcher broadcastDispatcher,
+            PipBoundsHandler pipBoundsHandler,
+            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+            PipTaskOrganizer pipTaskOrganizer) {
+        return new PipController(context, broadcastDispatcher, pipBoundsHandler,
+                pipSurfaceTransactionHelper, pipTaskOrganizer);
+    }
+
+    @SysUISingleton
+    @Provides
+    static SplitScreen provideSplitScreen(Context context,
+            DisplayController displayController, SystemWindows systemWindows,
+            DisplayImeController displayImeController, @Main Handler handler,
+            TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer) {
+        return new SplitScreenController(context, displayController, systemWindows,
+                displayImeController, handler, transactionPool, shellTaskOrganizer);
+    }
+
+    @SysUISingleton
+    @Provides
+    static PipNotification providePipNotification(Context context,
+            BroadcastDispatcher broadcastDispatcher,
+            PipController pipController) {
+        return new PipNotification(context, broadcastDispatcher, pipController);
+    }
+
+    @SysUISingleton
+    @Provides
+    static PipBoundsHandler providesPipBoundsHandler(Context context) {
+        return new PipBoundsHandler(context);
+    }
+
+    @SysUISingleton
+    @Provides
+    static PipTaskOrganizer providesPipTaskOrganizer(Context context,
+            PipBoundsHandler pipBoundsHandler,
+            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+            Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
+            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
+        return new PipTaskOrganizer(context, pipBoundsHandler,
+                pipSurfaceTransactionHelper, splitScreenOptional, displayController,
+                pipUiEventLogger, shellTaskOrganizer);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 4922600..98c0b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -16,20 +16,47 @@
 
 package com.android.systemui.wmshell;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
+import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
+import android.view.KeyEvent;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.pip.Pip;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.stackdivider.SplitScreen;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.tracing.ProtoTracer;
+import com.android.systemui.tracing.nano.SystemUiTraceProto;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.nano.WmShellTraceProto;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEvents;
+import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
+import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
+import com.android.wm.shell.protolog.ShellProtoLogImpl;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.Optional;
 
 import javax.inject.Inject;
@@ -38,19 +65,44 @@
  * Proxy in SysUiScope to delegate events to controllers in WM Shell library.
  */
 @SysUISingleton
-public final class WMShell extends SystemUI {
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+public final class WMShell extends SystemUI implements ProtoTraceable<SystemUiTraceProto> {
+    private final CommandQueue mCommandQueue;
     private final DisplayImeController mDisplayImeController;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final ActivityManagerWrapper mActivityManagerWrapper;
+    private final NavigationModeController mNavigationModeController;
+    private final ScreenLifecycle mScreenLifecycle;
+    private final SysUiState mSysUiState;
+    private final Optional<Pip> mPipOptional;
     private final Optional<SplitScreen> mSplitScreenOptional;
+    private final Optional<OneHanded> mOneHandedOptional;
+    private final ProtoTracer mProtoTracer;
 
     @Inject
-    WMShell(Context context, KeyguardUpdateMonitor keyguardUpdateMonitor,
+    public WMShell(Context context, CommandQueue commandQueue,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            ActivityManagerWrapper activityManagerWrapper,
             DisplayImeController displayImeController,
-            Optional<SplitScreen> splitScreenOptional) {
+            NavigationModeController navigationModeController,
+            ScreenLifecycle screenLifecycle,
+            SysUiState sysUiState,
+            Optional<Pip> pipOptional,
+            Optional<SplitScreen> splitScreenOptional,
+            Optional<OneHanded> oneHandedOptional,
+            ProtoTracer protoTracer) {
         super(context);
+        mCommandQueue = commandQueue;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mActivityManagerWrapper = activityManagerWrapper;
         mDisplayImeController = displayImeController;
+        mNavigationModeController = navigationModeController;
+        mScreenLifecycle = screenLifecycle;
+        mSysUiState = sysUiState;
+        mPipOptional = pipOptional;
         mSplitScreenOptional = splitScreenOptional;
+        mOneHandedOptional = oneHandedOptional;
+        mProtoTracer = protoTracer;
+        mProtoTracer.add(this);
     }
 
     @Override
@@ -59,11 +111,23 @@
         // constructor. And make sure the initialization of DisplayImeController won't depend on
         // specific feature anymore.
         mDisplayImeController.startMonitorDisplays();
-
+        mPipOptional.ifPresent(this::initPip);
         mSplitScreenOptional.ifPresent(this::initSplitScreen);
+        mOneHandedOptional.ifPresent(this::initOneHanded);
     }
 
-    private void initSplitScreen(SplitScreen splitScreen) {
+    @VisibleForTesting
+    void initPip(Pip pip) {
+        mCommandQueue.addCallback(new CommandQueue.Callbacks() {
+            @Override
+            public void showPictureInPictureMenu() {
+                pip.showPictureInPictureMenu();
+            }
+        });
+    }
+
+    @VisibleForTesting
+    void initSplitScreen(SplitScreen splitScreen) {
         mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
             @Override
             public void onKeyguardVisibilityChanged(boolean showing) {
@@ -75,7 +139,7 @@
             }
         });
 
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(
+        mActivityManagerWrapper.registerTaskStackListener(
                 new TaskStackChangeListener() {
                     @Override
                     public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
@@ -108,4 +172,141 @@
                     }
                 });
     }
+
+    @VisibleForTesting
+    void initOneHanded(OneHanded oneHanded) {
+        if (!oneHanded.hasOneHandedFeature()) {
+            return;
+        }
+
+        int currentMode = mNavigationModeController.addListener(mode ->
+                oneHanded.setThreeButtonModeEnabled(mode == NAV_BAR_MODE_3BUTTON));
+        oneHanded.setThreeButtonModeEnabled(currentMode == NAV_BAR_MODE_3BUTTON);
+
+        oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
+            @Override
+            public void onStartFinished(Rect bounds) {
+                mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
+                        true).commitUpdate(DEFAULT_DISPLAY);
+            }
+
+            @Override
+            public void onStopFinished(Rect bounds) {
+                mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
+                        false).commitUpdate(DEFAULT_DISPLAY);
+            }
+        });
+
+        oneHanded.registerGestureCallback(new OneHandedGestureEventCallback() {
+            @Override
+            public void onStart() {
+                if (oneHanded.isOneHandedEnabled()) {
+                    oneHanded.startOneHanded();
+                } else if (oneHanded.isSwipeToNotificationEnabled()) {
+                    mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
+                }
+            }
+
+            @Override
+            public void onStop() {
+                if (oneHanded.isOneHandedEnabled()) {
+                    oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
+                } else if (oneHanded.isSwipeToNotificationEnabled()) {
+                    mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
+                }
+            }
+        });
+
+        mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
+            @Override
+            public void onKeyguardBouncerChanged(boolean bouncer) {
+                if (bouncer) {
+                    oneHanded.stopOneHanded();
+                }
+            }
+
+            @Override
+            public void onKeyguardVisibilityChanged(boolean showing) {
+                oneHanded.stopOneHanded();
+            }
+        });
+
+        mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() {
+            @Override
+            public void onScreenTurningOff() {
+                oneHanded.stopOneHanded(
+                        OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT);
+            }
+        });
+
+        mCommandQueue.addCallback(new CommandQueue.Callbacks() {
+            @Override
+            public void onCameraLaunchGestureDetected(int source) {
+                oneHanded.stopOneHanded();
+            }
+
+            @Override
+            public void setImeWindowStatus(int displayId, IBinder token, int vis,
+                    int backDisposition, boolean showImeSwitcher) {
+                if (displayId == DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) != 0) {
+                    oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
+                }
+            }
+        });
+
+        mActivityManagerWrapper.registerTaskStackListener(
+                new TaskStackChangeListener() {
+                    @Override
+                    public void onTaskCreated(int taskId, ComponentName componentName) {
+                        oneHanded.stopOneHanded(
+                                OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT);
+                    }
+
+                    @Override
+                    public void onTaskMovedToFront(int taskId) {
+                        oneHanded.stopOneHanded(
+                                OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT);
+                    }
+                });
+    }
+
+    @Override
+    public void writeToProto(SystemUiTraceProto proto) {
+        if (proto.wmShell == null) {
+            proto.wmShell = new WmShellTraceProto();
+        }
+        // Dump to WMShell proto here
+        // TODO: Figure out how we want to synchronize while dumping to proto
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        // Handle commands if provided
+        for (int i = 0; i < args.length; i++) {
+            switch (args[i]) {
+                case "enable-text-logging": {
+                    String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+                    startTextLogging(groups);
+                    pw.println("Starting logging on groups: " + Arrays.toString(groups));
+                    return;
+                }
+                case "disable-text-logging": {
+                    String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+                    stopTextLogging(groups);
+                    pw.println("Stopping logging on groups: " + Arrays.toString(groups));
+                    return;
+                }
+            }
+        }
+
+        // Dump WMShell stuff here if no commands were handled
+    }
+
+    private void startTextLogging(String... groups) {
+        ShellProtoLogImpl.getSingleInstance().startTextLogging(mContext, groups);
+    }
+
+    private void stopTextLogging(String... groups) {
+        ShellProtoLogImpl.getSingleInstance().stopTextLogging(groups);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 0a3172ce..1f8cf95 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -18,19 +18,25 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.util.DisplayMetrics;
 import android.view.IWindowManager;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.pip.Pip;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
 import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.stackdivider.SplitScreen;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.FloatingContentCoordinator;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import dagger.BindsOptionalOf;
 import dagger.Module;
@@ -76,6 +82,13 @@
 
     @SysUISingleton
     @Provides
+    static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context,
+            ConfigurationController configController) {
+        return new PipSurfaceTransactionHelper(context, configController);
+    }
+
+    @SysUISingleton
+    @Provides
     static SystemWindows provideSystemWindows(DisplayController displayController,
             IWindowManager wmService) {
         return new SystemWindows(displayController, wmService);
@@ -89,6 +102,19 @@
         return organizer;
     }
 
+    @SysUISingleton
+    @Provides
+    static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
+            DisplayMetrics displayMetrics) {
+        return new FlingAnimationUtils.Builder(displayMetrics);
+    }
+
+    @BindsOptionalOf
+    abstract Pip optionalPip();
+
     @BindsOptionalOf
     abstract SplitScreen optionalSplitScreen();
+
+    @BindsOptionalOf
+    abstract OneHanded optionalOneHanded();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 7b45476..16fb2ca 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -20,17 +20,30 @@
 import android.os.Handler;
 import android.view.IWindowManager;
 
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.pip.phone.PipMenuActivity;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
-import com.android.systemui.stackdivider.SplitScreen;
-import com.android.systemui.stackdivider.SplitScreenController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.pip.Pip;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
+import com.android.systemui.pip.phone.PipController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.FloatingContentCoordinator;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+
+import java.util.Optional;
 
 import dagger.Module;
 import dagger.Provides;
@@ -50,12 +63,24 @@
         return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
     }
 
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
     @SysUISingleton
-    @PipMenuActivityClass
     @Provides
-    static Class<?> providePipMenuActivityClass() {
-        return PipMenuActivity.class;
+    static Pip providePipController(Context context,
+            BroadcastDispatcher broadcastDispatcher,
+            ConfigurationController configController,
+            DeviceConfigProxy deviceConfig,
+            DisplayController displayController,
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState,
+            PipBoundsHandler pipBoundsHandler,
+            PipSurfaceTransactionHelper surfaceTransactionHelper,
+            PipTaskOrganizer pipTaskOrganizer,
+            PipUiEventLogger pipUiEventLogger) {
+        return new PipController(context, broadcastDispatcher, configController, deviceConfig,
+                displayController, floatingContentCoordinator, sysUiState, pipBoundsHandler,
+                surfaceTransactionHelper,
+                pipTaskOrganizer,
+                pipUiEventLogger);
     }
 
     @SysUISingleton
@@ -67,4 +92,29 @@
         return new SplitScreenController(context, displayController, systemWindows,
                 displayImeController, handler, transactionPool, shellTaskOrganizer);
     }
+
+    @SysUISingleton
+    @Provides
+    static PipBoundsHandler providesPipBoundsHandler(Context context) {
+        return new PipBoundsHandler(context);
+    }
+
+    @SysUISingleton
+    @Provides
+    static PipTaskOrganizer providesPipTaskOrganizer(Context context,
+            PipBoundsHandler pipBoundsHandler,
+            PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+            Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
+            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
+        return new PipTaskOrganizer(context, pipBoundsHandler,
+                pipSurfaceTransactionHelper, splitScreenOptional, displayController,
+                pipUiEventLogger, shellTaskOrganizer);
+    }
+
+    @SysUISingleton
+    @Provides
+    static OneHanded provideOneHandedController(Context context,
+            DisplayController displayController) {
+        return OneHandedController.create(context, displayController);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index b7175ea..0fe3817 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -39,6 +39,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 
@@ -78,7 +79,7 @@
         Dependency.setInstance(mDependency);
         mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
                 mock(Executor.class), mock(DumpManager.class),
-                mock(BroadcastDispatcherLogger.class));
+                mock(BroadcastDispatcherLogger.class), mock(UserTracker.class));
 
         mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
         Instrumentation inst = spy(mRealInstrumentation);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index ac567e0..9079338 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -18,10 +18,12 @@
 
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
@@ -39,6 +41,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -62,6 +65,9 @@
     private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
     @Mock
     private ModeSwitchesController mModeSwitchesController;
+    @Mock
+    private RemoteCallback mRemoteCallback;
+    private ArgumentCaptor<Runnable> mRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
     private IWindowMagnificationConnection mIWindowMagnificationConnection;
     private WindowMagnification mWindowMagnification;
 
@@ -84,25 +90,24 @@
     }
 
     @Test
-    public void enableWindowMagnification() throws RemoteException {
+    public void enableWindowMagnification_passThrough() throws RemoteException {
         mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
-                Float.NaN);
+                Float.NaN, mRemoteCallback);
         waitForIdleSync();
 
-        verify(mWindowMagnificationAnimationController).enableWindowMagnification(3.0f, Float.NaN,
-                Float.NaN);
+        verify(mWindowMagnificationAnimationController).enableWindowMagnification(eq(3.0f),
+                eq(Float.NaN), eq(Float.NaN), mRunnableCaptor.capture());
+        verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
     }
 
     @Test
     public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
-        mIWindowMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
-                Float.NaN);
+        mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
         waitForIdleSync();
 
-        mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY);
-        waitForIdleSync();
-
-        verify(mWindowMagnificationAnimationController).deleteWindowMagnification();
+        verify(mWindowMagnificationAnimationController).deleteWindowMagnification(
+                mRunnableCaptor.capture());
+        verifyRunnableWrapsRemoteCallback(mRunnableCaptor.getValue());
     }
 
     @Test
@@ -138,5 +143,10 @@
 
         verify(mModeSwitchesController).removeButton(TEST_DISPLAY);
     }
+
+    private void verifyRunnableWrapsRemoteCallback(Runnable runnable) {
+        runnable.run();
+        verify(mRemoteCallback).sendResult(null);
+    }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index ee151c4..64e0673 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -18,6 +18,10 @@
 
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
 
@@ -27,6 +31,7 @@
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -35,7 +40,9 @@
 import android.content.pm.ActivityInfo;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewPropertyAnimator;
 import android.view.WindowManager;
 import android.widget.ImageView;
@@ -48,35 +55,38 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class MagnificationModeSwitchTest extends SysuiTestCase {
 
-    @Mock
-    private ImageView mMockImageView;
+    private ImageView mSpyImageView;
     @Mock
     private WindowManager mWindowManager;
     @Mock
     private ViewPropertyAnimator mViewPropertyAnimator;
     private MagnificationModeSwitch mMagnificationModeSwitch;
+    @Captor
+    private ArgumentCaptor<View.OnTouchListener> mTouchListenerCaptor;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        WindowManager wm = mContext.getSystemService(WindowManager.class);
+        doAnswer(invocation ->
+                wm.getMaximumWindowMetrics()
+        ).when(mWindowManager).getMaximumWindowMetrics();
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+        mSpyImageView = Mockito.spy(new ImageView(mContext));
+        doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
+                mTouchListenerCaptor.capture());
+        initMockImageViewAndAnimator();
 
-        when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
-        when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
-        when(mViewPropertyAnimator.setStartDelay(anyLong())).thenReturn(mViewPropertyAnimator);
-        when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
-                mViewPropertyAnimator);
-
-        when(mMockImageView.animate()).thenReturn(mViewPropertyAnimator);
-
-        mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mMockImageView);
+        mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView);
     }
 
     @Test
@@ -85,7 +95,7 @@
 
         mMagnificationModeSwitch.removeButton();
 
-        verify(mWindowManager).removeView(mMockImageView);
+        verify(mWindowManager).removeView(mSpyImageView);
         // First invocation is in showButton.
         verify(mViewPropertyAnimator, times(2)).cancel();
     }
@@ -94,22 +104,19 @@
     public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
 
-        verify(mMockImageView).setAlpha(1.0f);
-        verify(mMockImageView).setImageResource(
+        verify(mSpyImageView).setAlpha(1.0f);
+        verify(mSpyImageView).setImageResource(
                 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
-        verify(mViewPropertyAnimator).cancel();
-        verify(mViewPropertyAnimator).setDuration(anyLong());
-        verify(mViewPropertyAnimator).setStartDelay(anyLong());
-        verify(mViewPropertyAnimator).alpha(anyFloat());
+        assertShowButtonAnimation();
         ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
         verify(mViewPropertyAnimator).withEndAction(captor.capture());
-        verify(mWindowManager).addView(eq(mMockImageView), any(WindowManager.LayoutParams.class));
+        verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class));
 
         captor.getValue().run();
 
         // First invocation is in showButton.
         verify(mViewPropertyAnimator, times(2)).cancel();
-        verify(mWindowManager).removeView(mMockImageView);
+        verify(mWindowManager).removeView(mSpyImageView);
     }
 
     @Test
@@ -117,26 +124,134 @@
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
         mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
 
-        verify(mMockImageView, times(2)).setImageResource(
+        verify(mSpyImageView, times(2)).setImageResource(
                 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
     }
 
     @Test
-    public void performClick_fullscreenMode_removeViewAndChangeSettingsValue() {
-        ArgumentCaptor<View.OnClickListener> captor = ArgumentCaptor.forClass(
-                View.OnClickListener.class);
-        verify(mMockImageView).setOnClickListener(captor.capture());
+    public void performSingleTap_fullscreenMode_removeViewAndChangeSettingsValue() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        resetMockImageViewAndAnimator();
 
-        captor.getValue().onClick(mMockImageView);
+        // Perform a single-tap
+        final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, 0, ACTION_DOWN, 100, 100, 0));
+        verify(mViewPropertyAnimator).cancel();
 
-        // First invocation is in showButton.
-        verify(mViewPropertyAnimator, times(2)).cancel();
-        verify(mMockImageView).setImageResource(
+        resetMockImageViewAndAnimator();
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, ViewConfiguration.getTapTimeout(), ACTION_UP, 100, 100, 0));
+        verify(mViewPropertyAnimator).cancel();
+        verify(mSpyImageView).setImageResource(
                 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
-        verify(mWindowManager).removeView(mMockImageView);
+        verify(mWindowManager).removeView(mSpyImageView);
         final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
         assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, actualMode);
     }
+
+    @Test
+    public void showMagnificationButton_performDragging_updateViewLayout() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        resetMockImageViewAndAnimator();
+
+        // Perform dragging
+        final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+        final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, 0, ACTION_DOWN, 100, 100, 0));
+        verify(mSpyImageView).setAlpha(1.0f);
+        verify(mViewPropertyAnimator).cancel();
+
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0));
+        verify(mWindowManager).updateViewLayout(eq(mSpyImageView),
+                any(WindowManager.LayoutParams.class));
+
+        resetMockImageViewAndAnimator();
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0));
+        verify(mSpyImageView).setAlpha(1.0f);
+        assertModeUnchanged(previousMode);
+        assertShowButtonAnimation();
+    }
+
+    @Test
+    public void performSingleTapActionCanceled_showButtonAnimation() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        resetMockImageViewAndAnimator();
+
+        // Perform single tap
+        final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+        final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, 0, ACTION_DOWN, 100, 100, 0));
+
+        resetMockImageViewAndAnimator();
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0));
+        verify(mSpyImageView).setAlpha(1.0f);
+        assertModeUnchanged(previousMode);
+        assertShowButtonAnimation();
+    }
+
+    @Test
+    public void performDraggingActionCanceled_showButtonAnimation() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        resetMockImageViewAndAnimator();
+
+        // Perform dragging
+        final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+        final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, 0, ACTION_DOWN, 100, 100, 0));
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0));
+
+        resetMockImageViewAndAnimator();
+        listener.onTouch(mSpyImageView, MotionEvent.obtain(
+                0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0));
+        verify(mSpyImageView).setAlpha(1.0f);
+        assertModeUnchanged(previousMode);
+        assertShowButtonAnimation();
+    }
+
+    private void assertModeUnchanged(int expectedMode) {
+        final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+        assertEquals(expectedMode, actualMode);
+    }
+
+    private void assertShowButtonAnimation() {
+        verify(mViewPropertyAnimator).cancel();
+        verify(mViewPropertyAnimator).setDuration(anyLong());
+        verify(mViewPropertyAnimator).alpha(anyFloat());
+        verify(mViewPropertyAnimator).start();
+    }
+
+    private void initMockImageViewAndAnimator() {
+        when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
+        when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
+        when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
+                mViewPropertyAnimator);
+
+        when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator);
+        doAnswer(invocation -> {
+            Runnable run = invocation.getArgument(0);
+            run.run();
+            return null;
+        }).when(mSpyImageView).postDelayed(any(), anyLong());
+    }
+
+    private void resetMockImageViewAndAnimator() {
+        Mockito.reset(mViewPropertyAnimator);
+        Mockito.reset(mSpyImageView);
+        initMockImageViewAndAnimator();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index a6dfbbd..d74c62b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.os.Handler;
@@ -73,7 +74,10 @@
     SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
     @Mock
     WindowMagnifierCallback mWindowMagnifierCallback;
-
+    @Mock
+    Runnable mAnimationEndCallback;
+    @Mock
+    Runnable mAnimationEndCallback2;
     private SpyWindowMagnificationController mController;
     private WindowMagnificationController mSpyController;
     private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -101,8 +105,8 @@
     }
 
     @Test
-    public void enableWindowMagnification_disabled_expectedStartAndEndValues() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+    public void enableWindowMagnification_disabled_expectedValuesAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
 
         verify(mSpyController, atLeast(2)).enableWindowMagnification(
                 mScaleCaptor.capture(),
@@ -111,11 +115,28 @@
         verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
         verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
         verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+        verify(mAnimationEndCallback).run();
+    }
+
+
+    @Test
+    public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback() {
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationAnimationController.enableWindowMagnification(1,
+                            DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationEndCallback);
+                });
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController).enableWindowMagnification(1, DEFAULT_CENTER_X,
+                DEFAULT_CENTER_Y);
+        verify(mAnimationEndCallback).run();
     }
 
     @Test
-    public void enableWindowMagnification_enabling_expectedStartAndEndValues() {
-        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+    public void enableWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationEndCallback);
         final float targetScale = DEFAULT_SCALE + 1.0f;
         final float targetCenterX = DEFAULT_CENTER_X + 100;
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -123,7 +144,7 @@
         mInstrumentation.runOnMainSync(() -> {
             Mockito.reset(mSpyController);
             mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                    targetCenterX, targetCenterY);
+                    targetCenterX, targetCenterY, mAnimationEndCallback2);
             mCurrentScale.set(mController.getScale());
             mCurrentCenterX.set(mController.getCenterX());
             mCurrentCenterY.set(mController.getCenterY());
@@ -137,12 +158,33 @@
         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+        verify(mAnimationEndCallback, never()).run();
+        verify(mAnimationEndCallback2).run();
     }
 
     @Test
-    public void enableWindowMagnification_disabling_expectedStartAndEndValues() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
-        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+    public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationEndCallback);
+
+        mInstrumentation.runOnMainSync(() -> {
+            Mockito.reset(mSpyController);
+            mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
+                    Float.NaN, Float.NaN, mAnimationEndCallback2);
+        });
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+                anyFloat());
+        verify(mAnimationEndCallback, never()).run();
+        verify(mAnimationEndCallback2).run();
+    }
+
+    @Test
+    public void enableWindowMagnification_disabling_expectedValuesAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationEndCallback);
         final float targetScale = DEFAULT_SCALE + 1.0f;
         final float targetCenterX = DEFAULT_CENTER_X + 100;
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -151,11 +193,13 @@
                 () -> {
                     Mockito.reset(mSpyController);
                     mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
-                            targetCenterX, targetCenterY);
+                            targetCenterX, targetCenterY, mAnimationEndCallback2);
                     mCurrentScale.set(mController.getScale());
                     mCurrentCenterX.set(mController.getCenterX());
                     mCurrentCenterY.set(mController.getCenterY());
                 });
+        // Current spec shouldn't match given spec.
+        verify(mAnimationEndCallback2, never()).run();
         SystemClock.sleep(mWaitingAnimationPeriod);
 
         verify(mSpyController, atLeast(2)).enableWindowMagnification(
@@ -169,21 +213,73 @@
         assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
         assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+        verify(mAnimationEndCallback, never()).run();
+        verify(mAnimationEndCallback2).run();
     }
 
     @Test
-    public void enableWindowMagnificationWithSameScale_doNothing() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+    public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationEndCallback);
 
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        mInstrumentation.runOnMainSync(() -> {
+            Mockito.reset(mSpyController);
+            mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
+                    Float.NaN, Float.NaN, mAnimationEndCallback2);
+        });
+        SystemClock.sleep(mWaitingAnimationPeriod);
 
         verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
                 anyFloat());
+        verify(mSpyController, never()).deleteWindowMagnification();
+        verify(mAnimationEndCallback, never()).run();
+        verify(mAnimationEndCallback2).run();
+    }
+
+    @Test
+    public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationEndCallback);
+        final float targetScale = DEFAULT_SCALE + 1.0f;
+        final float targetCenterX = DEFAULT_CENTER_X + 100;
+        final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+        mInstrumentation.runOnMainSync(() -> {
+            Mockito.reset(mSpyController);
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY, mAnimationEndCallback2);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+        });
+
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verifyStartValue(mScaleCaptor, mCurrentScale.get());
+        verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
+        verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+        verify(mAnimationEndCallback, never()).run();
+        verify(mAnimationEndCallback2).run();
+    }
+
+    @Test
+    public void enableWindowMagnificationWithSameScale_enabled_doNothingButInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
+
+        verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+                anyFloat());
+        verify(mAnimationEndCallback).run();
     }
 
     @Test
     public void setScale_enabled_expectedScale() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
 
         mInstrumentation.runOnMainSync(
                 () -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1));
@@ -193,10 +289,10 @@
     }
 
     @Test
-    public void deleteWindowMagnification_enabled_expectedStartAndEndValues() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+    public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
 
-        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
 
         verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture());
@@ -205,24 +301,27 @@
         verifyStartValue(mCenterXCaptor, Float.NaN);
         verifyStartValue(mCenterYCaptor, Float.NaN);
         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+        verify(mAnimationEndCallback).run();
     }
 
     @Test
-    public void deleteWindowMagnification_disabled_doNothing() {
-        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+    public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback() {
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback);
 
         Mockito.verifyNoMoreInteractions(mSpyController);
+        verify(mAnimationEndCallback).run();
     }
 
     @Test
-    public void deleteWindowMagnification_enabling_checkStartAndEndValues() {
-        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+    public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationEndCallback);
 
-        //It just reverse the animation, so we don't need to wait the whole duration.
         mInstrumentation.runOnMainSync(
                 () -> {
                     Mockito.reset(mSpyController);
-                    mWindowMagnificationAnimationController.deleteWindowMagnification();
+                    mWindowMagnificationAnimationController.deleteWindowMagnification(
+                            mAnimationEndCallback2);
                     mCurrentScale.set(mController.getScale());
                     mCurrentCenterX.set(mController.getCenterX());
                     mCurrentCenterY.set(mController.getCenterY());
@@ -240,25 +339,30 @@
         verifyStartValue(mCenterXCaptor, Float.NaN);
         verifyStartValue(mCenterYCaptor, Float.NaN);
         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+        verify(mAnimationEndCallback, never()).run();
+        verify(mAnimationEndCallback2).run();
     }
 
     @Test
     public void deleteWindowMagnification_disabling_checkStartAndValues() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
-        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationEndCallback);
 
-        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationEndCallback2);
 
         verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture());
         verify(mSpyController).deleteWindowMagnification();
         assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+        verify(mAnimationEndCallback, never()).run();
+        verify(mAnimationEndCallback2).run();
     }
 
     @Test
     public void moveWindowMagnifier_enabled() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
 
         mInstrumentation.runOnMainSync(
                 () -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f));
@@ -273,6 +377,7 @@
 
         verify(mSpyController).onConfigurationChanged(100);
     }
+
     private void verifyFinalSpec(float expectedScale, float expectedCenterX,
             float expectedCenterY) {
         assertEquals(expectedScale, mController.getScale(), 0f);
@@ -280,21 +385,23 @@
         assertEquals(expectedCenterY, mController.getCenterY(), 0f);
     }
 
-    private void enableWindowMagnificationAndWaitAnimating(long duration) {
+    private void enableWindowMagnificationAndWaitAnimating(long duration,
+            @Nullable Runnable endCallback) {
         mInstrumentation.runOnMainSync(
                 () -> {
                     Mockito.reset(mSpyController);
                     mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
-                            DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+                            DEFAULT_CENTER_X, DEFAULT_CENTER_Y, endCallback);
                 });
         SystemClock.sleep(duration);
     }
 
-    private void deleteWindowMagnificationAndWaitAnimating(long duration) {
+    private void deleteWindowMagnificationAndWaitAnimating(long duration,
+            @Nullable Runnable endCallback) {
         mInstrumentation.runOnMainSync(
                 () -> {
                     resetMockObjects();
-                    mWindowMagnificationAnimationController.deleteWindowMagnification();
+                    mWindowMagnificationAnimationController.deleteWindowMagnification(endCallback);
                 });
         SystemClock.sleep(duration);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 22e9594..65301fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.BroadcastReceiver
 import android.content.Context
-import android.content.Intent
 import android.content.IntentFilter
 import android.os.Handler
 import android.os.Looper
@@ -30,6 +29,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import junit.framework.Assert.assertSame
@@ -80,6 +80,8 @@
     private lateinit var mockHandler: Handler
     @Mock
     private lateinit var logger: BroadcastDispatcherLogger
+    @Mock
+    private lateinit var userTracker: UserTracker
 
     private lateinit var executor: Executor
 
@@ -101,6 +103,7 @@
                 mock(Executor::class.java),
                 mock(DumpManager::class.java),
                 logger,
+                userTracker,
                 mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
 
         // These should be valid filters
@@ -178,11 +181,7 @@
 
     @Test
     fun testRegisterCurrentAsActualUser() {
-        val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
-            putExtra(Intent.EXTRA_USER_HANDLE, user1.identifier)
-        }
-        broadcastDispatcher.onReceive(mockContext, intent)
-        testableLooper.processAllMessages()
+        `when`(userTracker.userId).thenReturn(user1.identifier)
 
         broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
                 mockHandler, UserHandle.CURRENT)
@@ -250,8 +249,9 @@
         executor: Executor,
         dumpManager: DumpManager,
         logger: BroadcastDispatcherLogger,
+        userTracker: UserTracker,
         var mockUBRMap: Map<Int, UserBroadcastDispatcher>
-    ) : BroadcastDispatcher(context, bgLooper, executor, dumpManager, logger) {
+    ) : BroadcastDispatcher(context, bgLooper, executor, dumpManager, logger, userTracker) {
         override fun createUBRForUser(userId: Int): UserBroadcastDispatcher {
             return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java))
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 949932d..da00e7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.SysuiTestableContext
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
 import java.util.concurrent.Executor
 
 class FakeBroadcastDispatcher(
@@ -33,8 +34,9 @@
     looper: Looper,
     executor: Executor,
     dumpManager: DumpManager,
-    logger: BroadcastDispatcherLogger
-) : BroadcastDispatcher(context, looper, executor, dumpManager, logger) {
+    logger: BroadcastDispatcherLogger,
+    userTracker: UserTracker
+) : BroadcastDispatcher(context, looper, executor, dumpManager, logger, userTracker) {
 
     private val registeredReceivers = ArraySet<BroadcastReceiver>()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index a7808ad..f65c2c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -77,13 +77,13 @@
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -118,7 +118,7 @@
     @Mock
     private NotificationEntryManager mNotificationEntryManager;
     @Mock
-    private NotificationGroupManager mNotificationGroupManager;
+    private NotificationGroupManagerLegacy mNotificationGroupManager;
     @Mock
     private WindowManager mWindowManager;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 4936360..dd191e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -75,6 +75,7 @@
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -82,7 +83,6 @@
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -116,7 +116,7 @@
     @Mock
     private NotificationEntryManager mNotificationEntryManager;
     @Mock
-    private NotificationGroupManager mNotificationGroupManager;
+    private NotificationGroupManagerLegacy mNotificationGroupManager;
     @Mock
     private BubbleController.NotifCallback mNotifCallback;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 51ca2a4..58b27f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -30,8 +30,8 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -52,7 +52,7 @@
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
             NotificationLockscreenUserManager lockscreenUserManager,
-            NotificationGroupManager groupManager,
+            NotificationGroupManagerLegacy groupManager,
             NotificationEntryManager entryManager,
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index c8e0f49..b4af786 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -49,6 +49,7 @@
 import android.util.FeatureFlagUtils;
 import android.view.IWindowManager;
 import android.view.View;
+import android.view.WindowManagerPolicyConstants;
 import android.widget.FrameLayout;
 
 import androidx.test.filters.SmallTest;
@@ -69,7 +70,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -125,7 +126,7 @@
     @Mock GlobalActionsPanelPlugin mWalletPlugin;
     @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
     @Mock private Handler mHandler;
-    @Mock private CurrentUserContextTracker mCurrentUserContextTracker;
+    @Mock private UserContextProvider mUserContextProvider;
     private ControlsComponent mControlsComponent;
 
     private TestableLooper mTestableLooper;
@@ -137,7 +138,7 @@
         allowTestableLooperAsMainThread();
 
         when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
-        when(mCurrentUserContextTracker.getCurrentUserContext()).thenReturn(mContext);
+        when(mUserContextProvider.getUserContext()).thenReturn(mContext);
         mControlsComponent = new ControlsComponent(
                 true,
                 () -> mControlsController,
@@ -176,7 +177,7 @@
                 mSysUiState,
                 mHandler,
                 mControlsComponent,
-                mCurrentUserContextTracker
+                mUserContextProvider
         );
         mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
 
@@ -241,6 +242,28 @@
         verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS);
     }
 
+    @Test
+    public void testShouldShowScreenshot() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.integer.config_navBarInteractionMode,
+                WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON);
+
+        GlobalActionsDialog.ScreenshotAction screenshotAction =
+                mGlobalActionsDialog.makeScreenshotActionForTesting();
+        assertThat(screenshotAction.shouldShow()).isTrue();
+    }
+
+    @Test
+    public void testShouldNotShowScreenshot() {
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.integer.config_navBarInteractionMode,
+                WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
+
+        GlobalActionsDialog.ScreenshotAction screenshotAction =
+                mGlobalActionsDialog.makeScreenshotActionForTesting();
+        assertThat(screenshotAction.shouldShow()).isFalse();
+    }
+
     private void verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent event) {
         mTestableLooper.processAllMessages();
         verify(mUiEventLogger, times(1))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 89538ac..609b847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -74,7 +74,7 @@
 
         mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
                 new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null, true,
-                false, KEY, false);
+                false, KEY, false, false, false);
         mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index da1ec98..ef8d322 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -25,7 +25,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -34,6 +33,8 @@
     companion object {
         val LOCAL = true
         val RESUMPTION = true
+        val PLAYING = true
+        val UNDETERMINED = null
     }
 
     @Before
@@ -44,15 +45,13 @@
     @Test
     fun addPlayingThenRemote() {
         val playerIsPlaying = mock(MediaControlPanel::class.java)
-        whenever(playerIsPlaying.isPlaying).thenReturn(true)
-        val dataIsPlaying = createMediaData("app1", LOCAL, !RESUMPTION)
+        val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
 
         val playerIsRemote = mock(MediaControlPanel::class.java)
-        whenever(playerIsRemote.isPlaying).thenReturn(false)
-        val dataIsRemote = createMediaData("app2", !LOCAL, !RESUMPTION)
+        val dataIsRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
 
-        MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying)
         MediaPlayerData.addMediaPlayer("2", dataIsRemote, playerIsRemote)
+        MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying)
 
         val players = MediaPlayerData.players()
         assertThat(players).hasSize(2)
@@ -63,18 +62,16 @@
     @Ignore("Flaky")
     fun switchPlayersPlaying() {
         val playerIsPlaying1 = mock(MediaControlPanel::class.java)
-        whenever(playerIsPlaying1.isPlaying).thenReturn(true)
-        val dataIsPlaying1 = createMediaData("app1", LOCAL, !RESUMPTION)
+        var dataIsPlaying1 = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
 
         val playerIsPlaying2 = mock(MediaControlPanel::class.java)
-        whenever(playerIsPlaying2.isPlaying).thenReturn(false)
-        val dataIsPlaying2 = createMediaData("app2", LOCAL, !RESUMPTION)
+        var dataIsPlaying2 = createMediaData("app2", !PLAYING, LOCAL, !RESUMPTION)
 
         MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1)
         MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2)
 
-        whenever(playerIsPlaying1.isPlaying).thenReturn(false)
-        whenever(playerIsPlaying2.isPlaying).thenReturn(true)
+        dataIsPlaying1 = createMediaData("app1", !PLAYING, LOCAL, !RESUMPTION)
+        dataIsPlaying2 = createMediaData("app2", PLAYING, LOCAL, !RESUMPTION)
 
         MediaPlayerData.addMediaPlayer("1", dataIsPlaying1, playerIsPlaying1)
         MediaPlayerData.addMediaPlayer("2", dataIsPlaying2, playerIsPlaying2)
@@ -87,38 +84,43 @@
     @Test
     fun fullOrderTest() {
         val playerIsPlaying = mock(MediaControlPanel::class.java)
-        whenever(playerIsPlaying.isPlaying).thenReturn(true)
-        val dataIsPlaying = createMediaData("app1", LOCAL, !RESUMPTION)
+        val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
 
         val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java)
-        whenever(playerIsPlayingAndRemote.isPlaying).thenReturn(true)
-        val dataIsPlayingAndRemote = createMediaData("app2", !LOCAL, !RESUMPTION)
+        val dataIsPlayingAndRemote = createMediaData("app2", PLAYING, !LOCAL, !RESUMPTION)
 
         val playerIsStoppedAndLocal = mock(MediaControlPanel::class.java)
-        whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false)
-        val dataIsStoppedAndLocal = createMediaData("app3", LOCAL, !RESUMPTION)
+        val dataIsStoppedAndLocal = createMediaData("app3", !PLAYING, LOCAL, !RESUMPTION)
 
         val playerIsStoppedAndRemote = mock(MediaControlPanel::class.java)
-        whenever(playerIsStoppedAndLocal.isPlaying).thenReturn(false)
-        val dataIsStoppedAndRemote = createMediaData("app4", !LOCAL, !RESUMPTION)
+        val dataIsStoppedAndRemote = createMediaData("app4", !PLAYING, !LOCAL, !RESUMPTION)
 
         val playerCanResume = mock(MediaControlPanel::class.java)
-        whenever(playerCanResume.isPlaying).thenReturn(false)
-        val dataCanResume = createMediaData("app5", LOCAL, RESUMPTION)
+        val dataCanResume = createMediaData("app5", !PLAYING, LOCAL, RESUMPTION)
+
+        val playerUndetermined = mock(MediaControlPanel::class.java)
+        val dataUndetermined = createMediaData("app6", UNDETERMINED, LOCAL, RESUMPTION)
 
         MediaPlayerData.addMediaPlayer("3", dataIsStoppedAndLocal, playerIsStoppedAndLocal)
         MediaPlayerData.addMediaPlayer("5", dataIsStoppedAndRemote, playerIsStoppedAndRemote)
         MediaPlayerData.addMediaPlayer("4", dataCanResume, playerCanResume)
         MediaPlayerData.addMediaPlayer("1", dataIsPlaying, playerIsPlaying)
         MediaPlayerData.addMediaPlayer("2", dataIsPlayingAndRemote, playerIsPlayingAndRemote)
+        MediaPlayerData.addMediaPlayer("6", dataUndetermined, playerUndetermined)
 
         val players = MediaPlayerData.players()
-        assertThat(players).hasSize(5)
+        assertThat(players).hasSize(6)
         assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote,
-            playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote).inOrder()
+            playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote,
+            playerUndetermined).inOrder()
     }
 
-    private fun createMediaData(app: String, isLocalSession: Boolean, resumption: Boolean) =
+    private fun createMediaData(
+        app: String,
+        isPlaying: Boolean?,
+        isLocalSession: Boolean,
+        resumption: Boolean
+    ) =
         MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(), "",
-            null, null, null, true, null, isLocalSession, resumption, null, false)
+            null, null, null, true, null, isLocalSession, resumption, null, false, isPlaying)
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 57dbac5..ad5f987 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -52,7 +52,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -60,6 +59,7 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 389c5a0..f308e9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -68,7 +68,6 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -77,6 +76,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
deleted file mode 100644
index 756382a6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2020 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.onehanded;
-
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.wm.shell.common.DisplayController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OneHandedGestureHandlerTest extends OneHandedTestCase {
-    OneHandedTouchHandler mTouchHandler;
-    OneHandedTutorialHandler mTutorialHandler;
-    OneHandedGestureHandler mGestureHandler;
-    OneHandedController mOneHandedController;
-    @Mock
-    CommandQueue mCommandQueue;
-    @Mock
-    DisplayController mMockDisplayController;
-    @Mock
-    OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
-    @Mock
-    SysUiState mMockSysUiState;
-    @Mock
-    NavigationModeController mMockNavigationModeController;
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mTouchHandler = new OneHandedTouchHandler();
-        mTutorialHandler = new OneHandedTutorialHandler(mContext);
-        mGestureHandler = Mockito.spy(new OneHandedGestureHandler(
-                mContext, mMockDisplayController, mMockNavigationModeController));
-        mOneHandedController = new OneHandedController(
-                getContext(),
-                mCommandQueue,
-                mMockDisplayController,
-                mMockDisplayAreaOrganizer,
-                mTouchHandler,
-                mTutorialHandler,
-                mGestureHandler,
-                mMockSysUiState);
-    }
-
-    @Test
-    public void testOneHandedManager_registerForDisplayAreaOrganizer() {
-        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mGestureHandler);
-    }
-
-    @Test
-    public void testOneHandedManager_setGestureEventListener() {
-        verify(mGestureHandler).setGestureEventListener(any());
-
-        assertThat(mGestureHandler.mGestureEventCallback).isNotNull();
-    }
-
-    @Test
-    public void testReceiveNewConfig_whenSetOneHandedEnabled() {
-        // 1st called at init
-        verify(mGestureHandler).onOneHandedEnabled(true);
-        mOneHandedController.setOneHandedEnabled(true);
-        // 2nd called by setOneHandedEnabled()
-        verify(mGestureHandler, times(2)).onOneHandedEnabled(true);
-    }
-
-    @Test
-    public void testOneHandedDisabled_shouldDisposeInputChannel() {
-        mOneHandedController.setOneHandedEnabled(false);
-        mOneHandedController.setSwipeToNotificationEnabled(false);
-
-        assertThat(mGestureHandler.mInputMonitor).isNull();
-        assertThat(mGestureHandler.mInputEventReceiver).isNull();
-    }
-
-    @Test
-    public void testChangeNavBarTo2Button_shouldDisposeInputChannel() {
-        // 1st called at init
-        verify(mGestureHandler).onOneHandedEnabled(true);
-        mOneHandedController.setOneHandedEnabled(true);
-        // 2nd called by setOneHandedEnabled()
-        verify(mGestureHandler, times(2)).onOneHandedEnabled(true);
-
-        mGestureHandler.onNavigationModeChanged(NAV_BAR_MODE_2BUTTON);
-
-        assertThat(mGestureHandler.mInputMonitor).isNull();
-        assertThat(mGestureHandler.mInputEventReceiver).isNull();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
deleted file mode 100644
index 3c3ace0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2020 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.onehanded;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.wm.shell.common.DisplayController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OneHandedTouchHandlerTest extends OneHandedTestCase {
-    OneHandedTouchHandler mTouchHandler;
-    OneHandedTutorialHandler mTutorialHandler;
-    OneHandedGestureHandler mGestureHandler;
-    OneHandedController mOneHandedController;
-    @Mock
-    CommandQueue mCommandQueue;
-    @Mock
-    DisplayController mMockDisplayController;
-    @Mock
-    NavigationModeController mMockNavigationModeController;
-    @Mock
-    OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
-    @Mock
-    SysUiState mMockSysUiState;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mTouchHandler = Mockito.spy(new OneHandedTouchHandler());
-        mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
-                mMockNavigationModeController);
-        mOneHandedController = new OneHandedController(
-                getContext(),
-                mCommandQueue,
-                mMockDisplayController,
-                mMockDisplayAreaOrganizer,
-                mTouchHandler,
-                mTutorialHandler,
-                mGestureHandler,
-                mMockSysUiState);
-    }
-
-    @Test
-    public void testOneHandedManager_registerForDisplayAreaOrganizer() {
-        verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTouchHandler);
-    }
-
-    @Test
-    public void testOneHandedManager_registerTouchEventListener() {
-        verify(mTouchHandler).registerTouchEventListener(any());
-        assertThat(mTouchHandler.mTouchEventCallback).isNotNull();
-    }
-
-    @Test
-    public void testOneHandedDisabled_shouldDisposeInputChannel() {
-        mOneHandedController.setOneHandedEnabled(false);
-        assertThat(mTouchHandler.mInputMonitor).isNull();
-        assertThat(mTouchHandler.mInputEventReceiver).isNull();
-    }
-
-    @Test
-    public void testOneHandedEnabled_monitorInputChannel() {
-        mOneHandedController.setOneHandedEnabled(true);
-        assertThat(mTouchHandler.mInputMonitor).isNotNull();
-        assertThat(mTouchHandler.mInputEventReceiver).isNotNull();
-    }
-
-    @Test
-    public void testReceiveNewConfig_whenSetOneHandedEnabled() {
-        // 1st called at init
-        verify(mTouchHandler).onOneHandedEnabled(true);
-        mOneHandedController.setOneHandedEnabled(true);
-        // 2nd called by setOneHandedEnabled()
-        verify(mTouchHandler, times(2)).onOneHandedEnabled(true);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
deleted file mode 100644
index ae3df5d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2020 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.onehanded;
-
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Mockito.verify;
-
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.statusbar.CommandQueue;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OneHandedUITest extends OneHandedTestCase {
-    private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
-
-    CommandQueue mCommandQueue;
-    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    OneHandedUI mOneHandedUI;
-    ScreenLifecycle mScreenLifecycle;
-    @Mock
-    OneHandedController mOneHandedController;
-    @Mock
-    OneHandedTimeoutHandler mMockTimeoutHandler;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mCommandQueue = new CommandQueue(mContext);
-        mScreenLifecycle = new ScreenLifecycle();
-        mOneHandedUI = new OneHandedUI(mContext,
-                mCommandQueue,
-                mOneHandedController,
-                mScreenLifecycle);
-        mOneHandedUI.start();
-        mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
-    }
-
-    @Before
-    public void assumeOneHandedModeSupported() {
-        assumeTrue(SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false));
-    }
-
-    @Test
-    public void testStartOneHanded() {
-        mOneHandedUI.startOneHanded();
-
-        verify(mOneHandedController).startOneHanded();
-    }
-
-    @Test
-    public void testStopOneHanded() {
-        mOneHandedUI.stopOneHanded();
-
-        verify(mOneHandedController).stopOneHanded();
-    }
-
-    @Test
-    public void tesSettingsObserver_updateTapAppToExit() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.TAPS_APP_TO_EXIT, 1);
-
-        verify(mOneHandedController).setTaskChangeToExit(true);
-    }
-
-    @Test
-    public void tesSettingsObserver_updateEnabled() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
-
-        verify(mOneHandedController).setOneHandedEnabled(true);
-    }
-
-    @Test
-    public void tesSettingsObserver_updateTimeout() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
-                OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
-
-        verify(mMockTimeoutHandler).setTimeout(
-                OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
-    }
-
-    @Test
-    public void tesSettingsObserver_updateSwipeToNotification() {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1);
-
-        verify(mOneHandedController).setSwipeToNotificationEnabled(true);
-    }
-
-    @Ignore("Clarifying do not receive callback")
-    @Test
-    public void testKeyguardBouncerShowing_shouldStopOneHanded() {
-        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
-
-        verify(mOneHandedController).stopOneHanded();
-    }
-
-    @Test
-    public void testScreenTurningOff_shouldStopOneHanded() {
-        mScreenLifecycle.dispatchScreenTurningOff();
-
-        verify(mOneHandedController).stopOneHanded();
-    }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
index 4ba29e6..25fb7d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -96,22 +96,24 @@
     }
 
     @Test
-    fun testNotListeningByDefault() {
+    fun testNotListeningAllByDefault() {
         assertFalse(privacyItemController.allIndicatorsAvailable)
-        assertFalse(privacyItemController.micCameraAvailable)
+    }
 
-        verify(appOpsController, never()).addCallback(any(), any())
+    @Test
+    fun testMicCameraListeningByDefault() {
+        assertTrue(privacyItemController.micCameraAvailable)
     }
 
     @Test
     fun testMicCameraChanged() {
-        changeMicCamera(true)
+        changeMicCamera(false) // default is true
         executor.runAllReady()
 
-        verify(callback).onFlagMicCameraChanged(true)
+        verify(callback).onFlagMicCameraChanged(false)
         verify(callback, never()).onFlagAllChanged(anyBoolean())
 
-        assertTrue(privacyItemController.micCameraAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
         assertFalse(privacyItemController.allIndicatorsAvailable)
     }
 
@@ -124,20 +126,19 @@
         verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
 
         assertTrue(privacyItemController.allIndicatorsAvailable)
-        assertFalse(privacyItemController.micCameraAvailable)
     }
 
     @Test
     fun testBothChanged() {
         changeAll(true)
-        changeMicCamera(true)
+        changeMicCamera(false)
         executor.runAllReady()
 
         verify(callback, atLeastOnce()).onFlagAllChanged(true)
-        verify(callback, atLeastOnce()).onFlagMicCameraChanged(true)
+        verify(callback, atLeastOnce()).onFlagMicCameraChanged(false)
 
         assertTrue(privacyItemController.allIndicatorsAvailable)
-        assertTrue(privacyItemController.micCameraAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
     }
 
     @Test
@@ -157,18 +158,11 @@
     }
 
     @Test
-    fun testAll_listening() {
-        changeAll(true)
-        executor.runAllReady()
-
-        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
-    }
-
-    @Test
     fun testAllFalse_notListening() {
         changeAll(true)
         executor.runAllReady()
         changeAll(false)
+        changeMicCamera(false)
         executor.runAllReady()
 
         verify(appOpsController).removeCallback(any(), any())
@@ -176,8 +170,8 @@
 
     @Test
     fun testSomeListening_stillListening() {
+        // Mic and camera are true by default
         changeAll(true)
-        changeMicCamera(true)
         executor.runAllReady()
         changeAll(false)
         executor.runAllReady()
@@ -186,7 +180,8 @@
     }
 
     @Test
-    fun testAllDeleted_stopListening() {
+    fun testAllDeleted_micCameraFalse_stopListening() {
+        changeMicCamera(false)
         changeAll(true)
         executor.runAllReady()
         changeAll(null)
@@ -196,13 +191,13 @@
     }
 
     @Test
-    fun testMicDeleted_stopListening() {
+    fun testMicDeleted_stillListening() {
         changeMicCamera(true)
         executor.runAllReady()
         changeMicCamera(null)
         executor.runAllReady()
 
-        verify(appOpsController).removeCallback(any(), any())
+        verify(appOpsController, never()).removeCallback(any(), any())
     }
 
     private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index fb42baa..f152a74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -273,6 +273,7 @@
     @Test
     fun testNotListeningWhenIndicatorsDisabled() {
         changeAll(false)
+        changeMicCamera(false)
         privacyItemController.addCallback(callback)
         executor.runAllReady()
         verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 4c9e141..3e37fde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -33,7 +33,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import org.junit.Before;
@@ -62,7 +62,7 @@
     @Mock
     private Executor mExecutor;
     @Mock
-    private CurrentUserContextTracker mUserContextTracker;
+    private UserContextProvider mUserContextTracker;
     private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
         public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
                 boolean requiresShadeOpen) {
@@ -92,7 +92,7 @@
         doNothing().when(mRecordingService).startForeground(anyInt(), any());
         doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder();
 
-        doReturn(mContext).when(mUserContextTracker).getCurrentUserContext();
+        doReturn(mContext).when(mUserContextTracker).getUserContext();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt
deleted file mode 100644
index 628c06a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020 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.settings
-
-import android.content.Context
-import android.content.ContextWrapper
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class CurrentUserContextTrackerTest : SysuiTestCase() {
-
-    private lateinit var tracker: CurrentUserContextTracker
-    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        allowTestableLooperAsMainThread()
-
-        // wrap Context so that tests don't throw for missing package errors
-        val wrapped = object : ContextWrapper(context) {
-            override fun createContextAsUser(user: UserHandle, flags: Int): Context {
-                val mockContext = mock(Context::class.java)
-                `when`(mockContext.user).thenReturn(user)
-                `when`(mockContext.userId).thenReturn(user.identifier)
-                return mockContext
-            }
-        }
-
-        tracker = CurrentUserContextTracker(wrapped, broadcastDispatcher)
-        tracker.initialize()
-    }
-
-    @Test
-    fun testContextExistsAfterInit_noCrash() {
-        tracker.currentUserContext
-    }
-
-    @Test
-    fun testUserContextIsCorrectAfterUserSwitch() {
-        // We always start out with system ui test
-        assertTrue("Starting userId should be 0", tracker.currentUserContext.userId == 0)
-
-        // WHEN user changes
-        tracker.handleUserSwitched(1)
-
-        // THEN user context should have the correct userId
-        assertTrue("User has changed to userId 1, the context should reflect that",
-                tracker.currentUserContext.userId == 1)
-    }
-
-    @Suppress("UNUSED_PARAMETER")
-    @Test(expected = IllegalStateException::class)
-    fun testContextTrackerThrowsExceptionWhenNotInitialized() {
-        // GIVEN an uninitialized CurrentUserContextTracker
-        val userTracker = CurrentUserContextTracker(context, broadcastDispatcher)
-
-        // WHEN client asks for a context
-        val userContext = userTracker.currentUserContext
-
-        // THEN an exception is thrown
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
new file mode 100644
index 0000000..f76b50a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2020 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.settings
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UserTrackerImplTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var context: Context
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock(stubOnly = true)
+    private lateinit var dumpManager: DumpManager
+    @Mock(stubOnly = true)
+    private lateinit var handler: Handler
+
+    private val executor = Executor(Runnable::run)
+    private lateinit var tracker: UserTrackerImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(context.userId).thenReturn(UserHandle.USER_SYSTEM)
+        `when`(context.user).thenReturn(UserHandle.SYSTEM)
+        `when`(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation ->
+            val user = invocation.getArgument<UserHandle>(0)
+            `when`(context.user).thenReturn(user)
+            `when`(context.userId).thenReturn(user.identifier)
+            context
+        }
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val info = UserInfo(invocation.getArgument<Int>(0), "", UserInfo.FLAG_FULL)
+            listOf(info)
+        }
+
+        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+    }
+
+    @Test
+    fun testNotInitialized() {
+        assertThat(tracker.initialized).isFalse()
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserIdBeforeInitThrowsException() {
+        tracker.userId
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserHandleBeforeInitThrowsException() {
+        tracker.userHandle
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserContextBeforeInitThrowsException() {
+        tracker.userContext
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserContentResolverBeforeInitThrowsException() {
+        tracker.userContentResolver
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserProfilesBeforeInitThrowsException() {
+        tracker.userProfiles
+    }
+
+    @Test
+    fun testInitialize() {
+        tracker.initialize(0)
+
+        assertThat(tracker.initialized).isTrue()
+    }
+
+    @Test
+    fun testReceiverRegisteredOnInitialize() {
+        tracker.initialize(0)
+
+        val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
+
+        verify(context).registerReceiverForAllUsers(
+                eq(tracker), capture(captor), isNull(), eq(handler))
+    }
+
+    @Test
+    fun testInitialValuesSet() {
+        val testID = 4
+        tracker.initialize(testID)
+
+        verify(userManager).getProfiles(testID)
+
+        assertThat(tracker.userId).isEqualTo(testID)
+        assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID))
+        assertThat(tracker.userContext.userId).isEqualTo(testID)
+        assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID))
+        assertThat(tracker.userProfiles).hasSize(1)
+
+        val info = tracker.userProfiles[0]
+        assertThat(info.id).isEqualTo(testID)
+    }
+
+    @Test
+    fun testUserSwitch() {
+        tracker.initialize(0)
+        val newID = 5
+
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
+        tracker.onReceive(context, intent)
+
+        verify(userManager).getProfiles(newID)
+
+        assertThat(tracker.userId).isEqualTo(newID)
+        assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID))
+        assertThat(tracker.userContext.userId).isEqualTo(newID)
+        assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID))
+        assertThat(tracker.userProfiles).hasSize(1)
+
+        val info = tracker.userProfiles[0]
+        assertThat(info.id).isEqualTo(newID)
+    }
+
+    @Test
+    fun testManagedProfileAvailable() {
+        tracker.initialize(0)
+        val profileID = tracker.userId + 10
+
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val id = invocation.getArgument<Int>(0)
+            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+            val infoProfile = UserInfo(
+                    id + 10,
+                    "",
+                    "",
+                    UserInfo.FLAG_MANAGED_PROFILE,
+                    UserManager.USER_TYPE_PROFILE_MANAGED
+            )
+            infoProfile.profileGroupId = id
+            listOf(info, infoProfile)
+        }
+
+        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+        tracker.onReceive(context, intent)
+
+        assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
+    }
+
+    @Test
+    fun testCallbackNotCalledOnAdd() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+
+        tracker.addCallback(callback, executor)
+
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+        assertThat(callback.calledOnUserChanged).isEqualTo(0)
+    }
+
+    @Test
+    fun testCallbackCalledOnUserChanged() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+
+        val newID = 5
+
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
+        tracker.onReceive(context, intent)
+
+        assertThat(callback.calledOnUserChanged).isEqualTo(1)
+        assertThat(callback.lastUser).isEqualTo(newID)
+        assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
+    }
+
+    @Test
+    fun testCallbackCalledOnProfileChanged() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+        val profileID = tracker.userId + 10
+
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val id = invocation.getArgument<Int>(0)
+            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+            val infoProfile = UserInfo(
+                    id + 10,
+                    "",
+                    "",
+                    UserInfo.FLAG_MANAGED_PROFILE,
+                    UserManager.USER_TYPE_PROFILE_MANAGED
+            )
+            infoProfile.profileGroupId = id
+            listOf(info, infoProfile)
+        }
+
+        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+        tracker.onReceive(context, intent)
+
+        assertThat(callback.calledOnUserChanged).isEqualTo(0)
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
+    }
+
+    @Test
+    fun testCallbackRemoved() {
+        tracker.initialize(0)
+        val newID = 5
+        val profileID = newID + 10
+
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+        tracker.removeCallback(callback)
+
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5)
+        tracker.onReceive(context, intent)
+
+        val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+        tracker.onReceive(context, intentProfiles)
+
+        assertThat(callback.calledOnUserChanged).isEqualTo(0)
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+    }
+
+    private class TestCallback : UserTracker.Callback {
+        var calledOnUserChanged = 0
+        var calledOnProfilesChanged = 0
+        var lastUser: Int? = null
+        var lastUserContext: Context? = null
+        var lastUserProfiles = emptyList<UserInfo>()
+
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            calledOnUserChanged++
+            lastUser = newUser
+            lastUserContext = userContext
+        }
+
+        override fun onProfilesChanged(profiles: List<UserInfo>) {
+            calledOnProfilesChanged++
+            lastUserProfiles = profiles
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 8089561..b0f2a89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -70,6 +71,7 @@
                new Handler(TestableLooper.get(this).getLooper()));
     }
 
+    @Ignore("Causes binder calls which fail")
     @Test
     public void testNotificationManagementCodeHasNoDependencyOnStatusBarWindowManager() {
         mDependency.injectMockDependency(ShadeController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index d2bf483..d041ee0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -53,7 +54,6 @@
 import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import com.google.android.collect.Lists;
 
@@ -76,7 +76,7 @@
     // Dependency mocks:
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
-    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private NotificationGroupManagerLegacy mGroupManager;
     @Mock private VisualStabilityManager mVisualStabilityManager;
 
     private TestableLooper mTestableLooper;
@@ -95,7 +95,7 @@
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
                 mLockscreenUserManager);
-        mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
+        mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
         when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true);
         when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true);
@@ -136,11 +136,11 @@
                 Lists.newArrayList(entry0, entry1, entry2));
 
         // Set up group manager to report that they should be bundled now.
-        when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false);
-        when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(true);
-        when(mGroupManager.isChildInGroupWithSummary(entry2.getSbn())).thenReturn(true);
-        when(mGroupManager.getGroupSummary(entry1.getSbn())).thenReturn(entry0);
-        when(mGroupManager.getGroupSummary(entry2.getSbn())).thenReturn(entry0);
+        when(mGroupManager.isChildInGroup(entry0)).thenReturn(false);
+        when(mGroupManager.isChildInGroup(entry1)).thenReturn(true);
+        when(mGroupManager.isChildInGroup(entry2)).thenReturn(true);
+        when(mGroupManager.getGroupSummary(entry1)).thenReturn(entry0);
+        when(mGroupManager.getGroupSummary(entry2)).thenReturn(entry0);
 
         // Run updateNotifications - the view hierarchy should be reorganized.
         mViewHierarchyManager.updateNotificationViews();
@@ -165,9 +165,9 @@
                 Lists.newArrayList(entry0, entry1, entry2));
 
         // Set up group manager to report that they should not be bundled now.
-        when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false);
-        when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(false);
-        when(mGroupManager.isChildInGroupWithSummary(entry2.getSbn())).thenReturn(false);
+        when(mGroupManager.isChildInGroup(entry0)).thenReturn(false);
+        when(mGroupManager.isChildInGroup(entry1)).thenReturn(false);
+        when(mGroupManager.isChildInGroup(entry2)).thenReturn(false);
 
         // Run updateNotifications - the view hierarchy should be reorganized.
         mViewHierarchyManager.updateNotificationViews();
@@ -194,8 +194,8 @@
                 Lists.newArrayList(entry0, entry1));
 
         // Set up group manager to report a suppressed summary now.
-        when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false);
-        when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(false);
+        when(mGroupManager.isChildInGroup(entry0)).thenReturn(false);
+        when(mGroupManager.isChildInGroup(entry1)).thenReturn(false);
         when(mGroupManager.isSummaryOfSuppressedGroup(entry0.getSbn())).thenReturn(true);
 
         // Run updateNotifications - the view hierarchy should be reorganized.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index fc0201a..3e1616c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -75,13 +75,13 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationEntryManagerInflationTest;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
@@ -119,7 +119,7 @@
     @Mock private NotificationRemoveInterceptor mRemoveInterceptor;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private RankingMap mRankingMap;
-    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private NotificationGroupManagerLegacy mGroupManager;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private RowInflaterTask mAsyncInflationTask;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index 5a81d36..dfe006d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -48,10 +48,10 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.After;
@@ -109,8 +109,8 @@
                 eq(UID_ALLOW_DURING_SETUP)))
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
         mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
-        mDependency.injectTestDependency(NotificationGroupManager.class,
-                new NotificationGroupManager(
+        mDependency.injectTestDependency(NotificationGroupManagerLegacy.class,
+                new NotificationGroupManagerLegacy(
                         mock(StatusBarStateController.class),
                         () -> mock(PeopleNotificationIdentifier.class)));
         mDependency.injectMockDependency(ShadeController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 386c866c..14877ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -37,8 +37,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,12 +48,13 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class HighPriorityProviderTest extends SysuiTestCase {
     @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private GroupMembershipManager mGroupMembershipManager;
     private HighPriorityProvider mHighPriorityProvider;
 
     @Before
@@ -61,7 +62,7 @@
         MockitoAnnotations.initMocks(this);
         mHighPriorityProvider = new HighPriorityProvider(
                 mPeopleNotificationIdentifier,
-                mGroupManager);
+                mGroupMembershipManager);
     }
 
     @Test
@@ -71,7 +72,7 @@
                 .setImportance(IMPORTANCE_HIGH)
                 .build();
         when(mPeopleNotificationIdentifier
-                .getPeopleNotificationType(entry.getSbn(), entry.getRanking()))
+                .getPeopleNotificationType(entry))
                 .thenReturn(TYPE_NON_PERSON);
 
         // THEN it has high priority
@@ -88,7 +89,7 @@
                 .setImportance(IMPORTANCE_LOW)
                 .build();
         when(mPeopleNotificationIdentifier
-                .getPeopleNotificationType(entry.getSbn(), entry.getRanking()))
+                .getPeopleNotificationType(entry))
                 .thenReturn(TYPE_PERSON);
 
         // THEN it has high priority
@@ -105,7 +106,7 @@
                 .setNotification(notification)
                 .build();
         when(mPeopleNotificationIdentifier
-                .getPeopleNotificationType(entry.getSbn(), entry.getRanking()))
+                .getPeopleNotificationType(entry))
                 .thenReturn(TYPE_NON_PERSON);
 
         // THEN it has high priority
@@ -123,7 +124,7 @@
                 .setImportance(IMPORTANCE_LOW)
                 .build();
         when(mPeopleNotificationIdentifier
-                .getPeopleNotificationType(entry.getSbn(), entry.getRanking()))
+                .getPeopleNotificationType(entry))
                 .thenReturn(TYPE_NON_PERSON);
 
         // THEN it has high priority
@@ -141,7 +142,7 @@
                 .setImportance(IMPORTANCE_MIN)
                 .build();
         when(mPeopleNotificationIdentifier
-                .getPeopleNotificationType(entry.getSbn(), entry.getRanking()))
+                .getPeopleNotificationType(entry))
                 .thenReturn(TYPE_NON_PERSON);
 
         // THEN it does NOT have high priority
@@ -165,7 +166,7 @@
                 .setChannel(channel)
                 .build();
         when(mPeopleNotificationIdentifier
-                .getPeopleNotificationType(entry.getSbn(), entry.getRanking()))
+                .getPeopleNotificationType(entry))
                 .thenReturn(TYPE_PERSON);
 
         // THEN it does NOT have high priority
@@ -173,13 +174,13 @@
     }
 
     @Test
-    public void testIsHighPriority_checkChildrenToCalculatePriority() {
+    public void testIsHighPriority_checkChildrenToCalculatePriority_legacy() {
         // GIVEN: a summary with low priority has a highPriorityChild and a lowPriorityChild
         final NotificationEntry summary = createNotifEntry(false);
         final NotificationEntry lowPriorityChild = createNotifEntry(false);
         final NotificationEntry highPriorityChild = createNotifEntry(true);
-        when(mGroupManager.isGroupSummary(summary.getSbn())).thenReturn(true);
-        when(mGroupManager.getChildren(summary.getSbn())).thenReturn(
+        when(mGroupMembershipManager.isGroupSummary(summary)).thenReturn(true);
+        when(mGroupMembershipManager.getChildren(summary)).thenReturn(
                 new ArrayList<>(Arrays.asList(lowPriorityChild, highPriorityChild)));
 
         // THEN the summary is high priority since it has a high priority child
@@ -210,16 +211,20 @@
     }
 
     @Test
-    public void testIsHighPriority_checkChildrenToCalculatePriorityOf() {
+    public void testIsHighPriority_checkChildrenToCalculatePriority() {
         // GIVEN:
-        // GroupEntry = parentEntry, summary = lowPrioritySummary
+        // parent with summary = lowPrioritySummary
         //      NotificationEntry = lowPriorityChild
         //      NotificationEntry = highPriorityChild
+        final NotificationEntry lowPrioritySummary = createNotifEntry(false);
         final GroupEntry parentEntry = new GroupEntryBuilder()
-                .setSummary(createNotifEntry(false))
-                .addChild(createNotifEntry(false))
-                .addChild(createNotifEntry(true))
+                .setSummary(lowPrioritySummary)
                 .build();
+        when(mGroupMembershipManager.getChildren(parentEntry)).thenReturn(
+                new ArrayList<>(
+                        List.of(
+                                createNotifEntry(false),
+                                createNotifEntry(true))));
 
         // THEN the GroupEntry parentEntry is high priority since it has a high priority child
         assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
@@ -228,13 +233,15 @@
     @Test
     public void testIsHighPriority_childEntryRankingUpdated() {
         // GIVEN:
-        // GroupEntry = parentEntry, summary = lowPrioritySummary
+        // parent with summary = lowPrioritySummary
         //      NotificationEntry = lowPriorityChild
-        final NotificationEntry lowPriorityChild = createNotifEntry(false);
+        final NotificationEntry lowPrioritySummary = createNotifEntry(false);
         final GroupEntry parentEntry = new GroupEntryBuilder()
-                .setSummary(createNotifEntry(false))
-                .addChild(lowPriorityChild)
+                .setSummary(lowPrioritySummary)
                 .build();
+        final NotificationEntry lowPriorityChild = createNotifEntry(false);
+        when(mGroupMembershipManager.getChildren(parentEntry)).thenReturn(
+                new ArrayList<>(List.of(lowPriorityChild)));
 
         // WHEN the child entry ranking changes to high priority
         lowPriorityChild.setRanking(
@@ -243,9 +250,8 @@
                         .setImportance(IMPORTANCE_HIGH)
                         .build());
 
-        // THEN the parent entry's high priority value is updated - but not the parent's summary
+        // THEN the parent entry's high priority value is updated
         assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
-        assertFalse(mHighPriorityProvider.isHighPriority(parentEntry.getSummary()));
     }
 
     private NotificationEntry createNotifEntry(boolean highPriority) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 359faba..ce0f122 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -442,7 +442,7 @@
     }
 
     @Test
-    public void testDismissingLifetimeExtendedSummaryDoesNotDismissChildren() {
+    public void testRetractingLifetimeExtendedSummaryDoesNotDismissChildren() {
         // GIVEN A notif group with one summary and two children
         mCollection.addNotificationLifetimeExtender(mExtender1);
         CollectionEvent notif1 = postNotif(
@@ -460,15 +460,16 @@
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
         NotificationEntry entry3 = mCollectionListener.getEntry(notif3.key);
 
-        // GIVEN that the summary and one child are retracted, but both are lifetime-extended
+        // GIVEN that the summary and one child are retracted by the app, but both are
+        // lifetime-extended
         mExtender1.shouldExtendLifetime = true;
-        mNoMan.retractNotif(notif1.sbn, REASON_CANCEL);
-        mNoMan.retractNotif(notif2.sbn, REASON_CANCEL);
+        mNoMan.retractNotif(notif1.sbn, REASON_APP_CANCEL);
+        mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
         assertEquals(
                 new ArraySet<>(List.of(entry1, entry2, entry3)),
                 new ArraySet<>(mCollection.getAllNotifs()));
 
-        // WHEN the summary is dismissed by the user
+        // WHEN the summary is retracted by the app
         mCollection.dismissNotification(entry1, defaultStats(entry1));
 
         // THEN the summary is removed, but both children stick around
@@ -480,6 +481,28 @@
     }
 
     @Test
+    public void testNMSReportsUserDismissalAlwaysRemovesNotif() throws RemoteException {
+        // GIVEN notifications are lifetime extended
+        mExtender1.shouldExtendLifetime = true;
+        CollectionEvent notif = postNotif(buildNotif(TEST_PACKAGE, 1, "myTag"));
+        CollectionEvent notif2 = postNotif(buildNotif(TEST_PACKAGE, 2, "myTag"));
+        NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+        NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+        assertEquals(
+                new ArraySet<>(List.of(entry, entry2)),
+                new ArraySet<>(mCollection.getAllNotifs()));
+
+        // WHEN the notifications are reported to be dismissed by the user by NMS
+        mNoMan.retractNotif(notif.sbn, REASON_CANCEL);
+        mNoMan.retractNotif(notif2.sbn, REASON_CLICK);
+
+        // THEN the notifications are removed b/c they were dismissed by the user
+        assertEquals(
+                new ArraySet<>(List.of()),
+                new ArraySet<>(mCollection.getAllNotifs()));
+    }
+
+    @Test
     public void testDismissNotificationCallsDismissInterceptors() throws RemoteException {
         // GIVEN a collection with notifications with multiple dismiss interceptors
         mInterceptor1.shouldInterceptDismissal = true;
@@ -833,13 +856,13 @@
         NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
-        // WHEN a notification is removed
-        mNoMan.retractNotif(notif2.sbn, REASON_CLICK);
+        // WHEN a notification is removed by the app
+        mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
 
         // THEN each extender is asked whether to extend, even if earlier ones return true
-        verify(mExtender1).shouldExtendLifetime(entry2, REASON_CLICK);
-        verify(mExtender2).shouldExtendLifetime(entry2, REASON_CLICK);
-        verify(mExtender3).shouldExtendLifetime(entry2, REASON_CLICK);
+        verify(mExtender1).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender2).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+        verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry is not removed
         assertTrue(mCollection.getAllNotifs().contains(entry2));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 82a7774..c832fe4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -40,7 +40,7 @@
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
-import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.google.common.truth.Truth.assertThat
 import dagger.Lazy
@@ -71,14 +71,14 @@
         notificationFilter = mock(NotificationFilter::class.java)
         rankingManager = TestableNotificationRankingManager(
                 lazyMedia,
-                mock(NotificationGroupManager::class.java),
+                mock(NotificationGroupManagerLegacy::class.java),
                 mock(HeadsUpManager::class.java),
                 notificationFilter,
                 mock(NotificationEntryManagerLogger::class.java),
                 sectionsManager,
                 personNotificationIdentifier,
                 HighPriorityProvider(personNotificationIdentifier,
-                    mock(NotificationGroupManager::class.java))
+                    mock(NotificationGroupManagerLegacy::class.java))
         )
     }
 
@@ -174,7 +174,7 @@
                 .setOverrideGroupKey("")
                 .build()
 
-        whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking))
+        whenever(personNotificationIdentifier.getPeopleNotificationType(a))
                 .thenReturn(TYPE_IMPORTANT_PERSON)
 
         val bN = Notification.Builder(mContext, "test")
@@ -194,7 +194,7 @@
             whenever(it.isHeadsUp).thenReturn(true)
         }
 
-        whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking))
+        whenever(personNotificationIdentifier.getPeopleNotificationType(a))
                 .thenReturn(TYPE_PERSON)
 
         assertEquals(listOf(b, a), rankingManager.updateRanking(null, listOf(a, b), "test"))
@@ -216,7 +216,7 @@
                 .setUser(mContext.user)
                 .setOverrideGroupKey("")
                 .build()
-        whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking))
+        whenever(personNotificationIdentifier.getPeopleNotificationType(a))
                 .thenReturn(TYPE_PERSON)
 
         val bN = Notification.Builder(mContext, "test")
@@ -232,7 +232,7 @@
                 .setUser(mContext.user)
                 .setOverrideGroupKey("")
                 .build()
-        whenever(personNotificationIdentifier.getPeopleNotificationType(b.sbn, b.ranking))
+        whenever(personNotificationIdentifier.getPeopleNotificationType(b))
                 .thenReturn(TYPE_IMPORTANT_PERSON)
 
         whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_IMPORTANT_PERSON))
@@ -261,7 +261,7 @@
                 .setUser(mContext.user)
                 .setOverrideGroupKey("")
                 .build()
-        whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking))
+        whenever(personNotificationIdentifier.getPeopleNotificationType(a))
                 .thenReturn(TYPE_PERSON)
 
         val bN = Notification.Builder(mContext, "test")
@@ -277,7 +277,7 @@
                 .setUser(mContext.user)
                 .setOverrideGroupKey("")
                 .build()
-        whenever(personNotificationIdentifier.getPeopleNotificationType(b.sbn, b.ranking))
+        whenever(personNotificationIdentifier.getPeopleNotificationType(b))
                 .thenReturn(TYPE_FULL_PERSON)
 
         whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_FULL_PERSON))
@@ -400,7 +400,7 @@
                 .setUser(mContext.user)
                 .setOverrideGroupKey("")
                 .build()
-        whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking))
+        whenever(personNotificationIdentifier.getPeopleNotificationType(a))
                 .thenReturn(TYPE_IMPORTANT_PERSON)
 
         assertThat(rankingManager.updateRanking(null, listOf(a, b, c), "test"))
@@ -410,7 +410,7 @@
 
     internal class TestableNotificationRankingManager(
         mediaManager: Lazy<NotificationMediaManager>,
-        groupManager: NotificationGroupManager,
+        groupManager: NotificationGroupManagerLegacy,
         headsUpManager: HeadsUpManager,
         filter: NotificationFilter,
         logger: NotificationEntryManagerLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index c49393d..711f0ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
 import org.junit.Assert.assertFalse
@@ -47,12 +48,10 @@
     private lateinit var promoter: NotifPromoter
     private lateinit var peopleSectioner: NotifSectioner
 
-    @Mock
-    private lateinit var pipeline: NotifPipeline
-    @Mock
-    private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
-    @Mock
-    private lateinit var channel: NotificationChannel
+    @Mock private lateinit var pipeline: NotifPipeline
+    @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
+    @Mock private lateinit var channel: NotificationChannel
+    @Mock private lateinit var headerController: NodeController
     private lateinit var entry: NotificationEntry
 
     private lateinit var coordinator: ConversationCoordinator
@@ -60,7 +59,7 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        coordinator = ConversationCoordinator(peopleNotificationIdentifier)
+        coordinator = ConversationCoordinator(peopleNotificationIdentifier, headerController)
         whenever(channel.isImportantConversation).thenReturn(true)
 
         coordinator.attach(pipeline)
@@ -84,8 +83,8 @@
 
     @Test
     fun testInPeopleSection() {
-        whenever(peopleNotificationIdentifier.getPeopleNotificationType(
-            entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON)
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry))
+            .thenReturn(TYPE_PERSON)
 
         // only put people notifications in this section
         assertTrue(peopleSectioner.isInSection(entry))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
index fa992a5..7e771ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
@@ -73,6 +74,7 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+    @Mock private NodeController mHeaderController;
 
     private NotificationEntry mEntry;
 
@@ -85,8 +87,8 @@
                 mHeadsUpManager,
                 mHeadsUpViewBinder,
                 mNotificationInterruptStateProvider,
-                mRemoteInputManager
-        );
+                mRemoteInputManager,
+                mHeaderController);
 
         mCoordinator.attach(mNotifPipeline);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 3a7d28a..1031d6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.render.NodeController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,6 +55,8 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private HighPriorityProvider mHighPriorityProvider;
     @Mock private NotifPipeline mNotifPipeline;
+    @Mock private NodeController mAlertingHeaderController;
+    @Mock private NodeController mSilentHeaderController;
 
     @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
 
@@ -67,8 +70,9 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        RankingCoordinator rankingCoordinator =
-                new RankingCoordinator(mStatusBarStateController, mHighPriorityProvider);
+        RankingCoordinator rankingCoordinator = new RankingCoordinator(
+                mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
+                mSilentHeaderController);
         mEntry = new NotificationEntryBuilder().build();
 
         rankingCoordinator.attach(mNotifPipeline);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 5aeb43f..edb8776 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -71,6 +72,7 @@
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private NotificationMenuRow mMenuRow;
     @Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
+    @Mock private GroupMembershipManager mGroupMembershipManager;
 
     @Before
     public void setUp() {
@@ -89,7 +91,8 @@
         mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
 
         mBlockingHelperManager = new NotificationBlockingHelperManager(
-                mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class));
+                mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class),
+                mGroupMembershipManager);
         // By default, have the shade visible/expanded.
         mBlockingHelperManager.setNotificationShadeExpanded(1f);
     }
@@ -185,6 +188,7 @@
                 .build();
         assertFalse(childRow.getIsNonblockable());
 
+        when(mGroupMembershipManager.isOnlyChildInGroup(childRow.getEntry())).thenReturn(true);
         assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
 
         verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 7a0a19b..aff8ade 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -67,6 +67,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.icon.IconBuilder;
 import com.android.systemui.statusbar.notification.icon.IconManager;
@@ -77,7 +78,6 @@
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -130,7 +130,8 @@
     @Mock private KeyguardBypassController mKeyguardBypassController;
     @Mock private StatusBarStateController mStatusBarStateController;
 
-    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private NotificationGroupManagerLegacy mGroupMembershipManager;
+    @Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private LeakDetector mLeakDetector;
 
@@ -170,10 +171,10 @@
 
         mEntryManager = new NotificationEntryManager(
                 mock(NotificationEntryManagerLogger.class),
-                mGroupManager,
+                mGroupMembershipManager,
                 new NotificationRankingManager(
                         () -> mock(NotificationMediaManager.class),
-                        mGroupManager,
+                        mGroupMembershipManager,
                         mHeadsUpManager,
                         mock(NotificationFilter.class),
                         mock(NotificationEntryManagerLogger.class),
@@ -228,7 +229,8 @@
                                 new FakeSystemClock(),
                                 "FOOBAR", "FOOBAR",
                                 mKeyguardBypassController,
-                                mGroupManager,
+                                mGroupMembershipManager,
+                                mGroupExpansionManager,
                                 mRowContentBindStage,
                                 mock(NotificationLogger.class),
                                 mHeadsUpManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index c2c40ca..e1668ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -69,7 +69,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
@@ -128,7 +128,7 @@
     @Mock private ShortcutManager mShortcutManager;
     @Mock private ChannelEditorDialogController mChannelEditorDialogController;
     @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-    @Mock private CurrentUserContextTracker mContextTracker;
+    @Mock private UserContextProvider mContextTracker;
     @Mock private BubbleController mBubbleController;
     @Mock(answer = Answers.RETURNS_SELF)
     private PriorityOnboardingDialogController.Builder mBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index df26c5b..b952c05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.icon.IconBuilder;
@@ -68,7 +69,6 @@
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
 
 import org.mockito.ArgumentCaptor;
@@ -96,7 +96,8 @@
     private final Context mContext;
     private final TestableLooper mTestLooper;
     private int mId;
-    private final NotificationGroupManager mGroupManager;
+    private final NotificationGroupManagerLegacy mGroupMembershipManager;
+    private final NotificationGroupManagerLegacy mGroupExpansionManager;
     private ExpandableNotificationRow mRow;
     private HeadsUpManagerPhone mHeadsUpManager;
     private final NotifBindPipeline mBindPipeline;
@@ -116,13 +117,14 @@
         dependency.injectMockDependency(BubbleController.class);
         dependency.injectMockDependency(NotificationShadeWindowController.class);
         mStatusBarStateController = mock(StatusBarStateController.class);
-        mGroupManager = new NotificationGroupManager(
+        mGroupMembershipManager = new NotificationGroupManagerLegacy(
                 mStatusBarStateController,
                 () -> mock(PeopleNotificationIdentifier.class));
+        mGroupExpansionManager = mGroupMembershipManager;
         mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
-                mock(KeyguardBypassController.class), mock(NotificationGroupManager.class),
+                mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class),
                 mock(ConfigurationControllerImpl.class));
-        mGroupManager.setHeadsUpManager(mHeadsUpManager);
+        mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager);
         mIconManager = new IconManager(
                 mock(CommonNotifCollection.class),
                 mock(LauncherApps.class),
@@ -416,7 +418,8 @@
                 entry.getKey(),
                 mock(ExpansionLogger.class),
                 mock(KeyguardBypassController.class),
-                mGroupManager,
+                mGroupMembershipManager,
+                mGroupExpansionManager,
                 mHeadsUpManager,
                 mBindStage,
                 mock(OnExpandClickListener.class),
@@ -434,7 +437,7 @@
         // This would be done as part of onAsyncInflationFinished, but we skip large amounts of
         // the callback chain, so we need to make up for not adding it to the group manager
         // here.
-        mGroupManager.onEntryAdded(entry);
+        mGroupMembershipManager.onEntryAdded(entry);
         return row;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 7ca2478..8cd7103 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -63,6 +64,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -87,6 +89,10 @@
     @Mock private NotificationRowComponent mNotificationRowComponent;
     @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
     @Mock private NotificationSectionsLogger mLogger;
+    @Mock private SectionHeaderController mIncomingHeaderController;
+    @Mock private SectionHeaderController mPeopleHeaderController;
+    @Mock private SectionHeaderController mAlertingHeaderController;
+    @Mock private SectionHeaderController mSilentHeaderController;
 
     private NotificationSectionsManager mSectionsManager;
 
@@ -109,15 +115,21 @@
                 });
         when(mNotificationRowComponent.getActivatableNotificationViewController())
                 .thenReturn(mActivatableNotificationViewController);
+        when(mIncomingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
+        when(mPeopleHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
+        when(mAlertingHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
+        when(mSilentHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
         mSectionsManager =
                 new NotificationSectionsManager(
-                        mActivityStarterDelegate,
                         mStatusBarStateController,
                         mConfigurationController,
-                        mPeopleHubAdapter,
                         mKeyguardMediaController,
                         mSectionsFeatureManager,
-                        mLogger
+                        mLogger,
+                        mIncomingHeaderController,
+                        mPeopleHeaderController,
+                        mAlertingHeaderController,
+                        mSilentHeaderController
                 );
         // Required in order for the header inflation to work properly
         when(mNssl.generateLayoutParams(any(AttributeSet.class)))
@@ -241,8 +253,9 @@
         mSectionsManager.updateSectionBoundaries();
         clearInvocations(mNssl);
 
+        SectionHeaderView silentHeaderView = mSectionsManager.getSilentHeaderView();
         ViewGroup transientParent = mock(ViewGroup.class);
-        mSectionsManager.getSilentHeaderView().setTransientContainer(transientParent);
+        when(silentHeaderView.getTransientContainer()).thenReturn(transientParent);
 
         // WHEN the LO section reappears
         setStackState(
@@ -252,8 +265,8 @@
 
         // THEN the header is first removed from the transient parent before being added to the
         // NSSL.
-        verify(transientParent).removeTransientView(mSectionsManager.getSilentHeaderView());
-        verify(mNssl).addView(mSectionsManager.getSilentHeaderView(), 1);
+        verify(transientParent).removeTransientView(silentHeaderView);
+        verify(mNssl).addView(silentHeaderView, 1);
     }
 
     @Test
@@ -358,23 +371,6 @@
     }
 
     @Test
-    public void testPeopleFiltering_keepPeopleHeaderWhenSectionEmpty() {
-        mSectionsManager.setPeopleHubVisible(true);
-        enablePeopleFiltering();
-
-        setStackState(
-                PEOPLE_HEADER,
-                ALERTING_HEADER,
-                ALERTING,
-                GENTLE_HEADER,
-                GENTLE);
-        mSectionsManager.updateSectionBoundaries();
-
-        verify(mNssl, never()).removeView(mSectionsManager.getPeopleHeaderView());
-        verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
-    }
-
-    @Test
     public void testPeopleFiltering_AlertingHunWhilePeopleVisible() {
         enablePeopleFiltering();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index fa9ea6b..de59ac31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -47,9 +47,6 @@
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -72,18 +69,15 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.leak.LeakDetector;
@@ -117,12 +111,12 @@
     @Mock private SysuiStatusBarStateController mBarState;
     @Mock private HeadsUpManagerPhone mHeadsUpManager;
     @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
-    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private NotificationGroupManagerLegacy mGroupMembershipManger;
+    @Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
     @Mock private ExpandHelper mExpandHelper;
     @Mock private EmptyShadeView mEmptyShadeView;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
-    @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private MetricsLogger mMetricsLogger;
     @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
     @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
@@ -131,6 +125,7 @@
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
     @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
+    @Mock NotificationStackScrollLayoutController mStackScrollLayoutController;
     private NotificationEntryManager mEntryManager;
     private int mOriginalInterruptionModelSetting;
     private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
@@ -162,10 +157,10 @@
 
         mEntryManager = new NotificationEntryManager(
                 mock(NotificationEntryManagerLogger.class),
-                mock(NotificationGroupManager.class),
+                mock(NotificationGroupManagerLegacy.class),
                 new NotificationRankingManager(
                         () -> mock(NotificationMediaManager.class),
-                        mGroupManager,
+                        mGroupMembershipManger,
                         mHeadsUpManager,
                         mock(NotificationFilter.class),
                         mock(NotificationEntryManagerLogger.class),
@@ -203,9 +198,6 @@
                 mNotificationRoundnessManager,
                 mock(DynamicPrivacyController.class),
                 mStatusBarStateController,
-                mHeadsUpManager,
-                new FalsingManagerFake(),
-                mock(NotificationGutsManager.class),
                 mNotificationSectionsManager,
                 mock(ForegroundServiceSectionController.class),
                 mock(ForegroundServiceDismissalFeatureController.class),
@@ -213,16 +205,19 @@
                 mock(NotifPipeline.class),
                 mEntryManager,
                 mock(NotifCollection.class),
-                mUiEventLoggerFake
+                mUiEventLoggerFake,
+                mGroupMembershipManger,
+                mGroupExpansionManager
         );
         mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
                 mNotificationSwipeHelper);
         mStackScroller = spy(mStackScrollerInternal);
         mStackScroller.setShelfController(notificationShelfController);
         mStackScroller.setStatusBar(mBar);
-        mStackScroller.setScrimController(mock(ScrimController.class));
-        mStackScroller.setGroupManager(mGroupManager);
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
+        when(mStackScrollLayoutController.getNoticationRoundessManager())
+                .thenReturn(mock(NotificationRoundnessManager.class));
+        mStackScroller.setController(mStackScrollLayoutController);
 
         // Stub out functionality that isn't necessary to test.
         doNothing().when(mBar)
@@ -231,7 +226,7 @@
                         anyBoolean(),
                         anyBoolean(),
                         anyBoolean());
-        doNothing().when(mGroupManager).collapseAllGroups();
+        doNothing().when(mGroupExpansionManager).collapseGroups();
         doNothing().when(mExpandHelper).cancelImmediately();
         doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
     }
@@ -454,6 +449,60 @@
                 .DISMISS_SILENT_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0));
     }
 
+    @Test
+    public void testAddNotificationUpdatesSpeedBumpIndex() {
+        // initial state calculated == 0
+        assertEquals(0, mStackScroller.getSpeedBumpIndex());
+
+        // add notification that's before the speed bump
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(row.getEntry()).thenReturn(entry);
+        when(entry.isAmbient()).thenReturn(false);
+        mStackScroller.addContainerView(row);
+
+        // speed bump = 1
+        assertEquals(1, mStackScroller.getSpeedBumpIndex());
+    }
+
+    @Test
+    public void testAddAmbientNotificationNoSpeedBumpUpdate() {
+        // initial state calculated  == 0
+        assertEquals(0, mStackScroller.getSpeedBumpIndex());
+
+        // add notification that's after the speed bump
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(row.getEntry()).thenReturn(entry);
+        when(entry.isAmbient()).thenReturn(true);
+        mStackScroller.addContainerView(row);
+
+        // speed bump is set to 0
+        assertEquals(0, mStackScroller.getSpeedBumpIndex());
+    }
+
+    @Test
+    public void testRemoveNotificationUpdatesSpeedBump() {
+        // initial state calculated == 0
+        assertEquals(0, mStackScroller.getSpeedBumpIndex());
+
+        // add 3 notification that are after the speed bump
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+        NotificationEntry entry = mock(NotificationEntry.class);
+        when(row.getEntry()).thenReturn(entry);
+        when(entry.isAmbient()).thenReturn(false);
+        mStackScroller.addContainerView(row);
+
+        // speed bump is 1
+        assertEquals(1, mStackScroller.getSpeedBumpIndex());
+
+        // remove the notification that was before the speed bump
+        mStackScroller.removeContainerView(row);
+
+        // speed bump is now 0
+        assertEquals(0, mStackScroller.getSpeedBumpIndex());
+    }
+
     private void setBarStateForTest(int state) {
         // Can't inject this through the listener or we end up on the actual implementation
         // rather than the mock because the spy just coppied the anonymous inner /shruggie.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 4fffd3e..ef11f81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
@@ -45,10 +48,13 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -60,6 +66,7 @@
 import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -70,46 +77,33 @@
 @RunWith(AndroidTestingRunner.class)
 public class NotificationStackScrollerControllerTest extends SysuiTestCase {
 
-    @Mock
-    private NotificationGutsManager mNotificationGutsManager;
-    @Mock
-    private HeadsUpManagerPhone mHeadsUpManager;
-    @Mock
-    private NotificationRoundnessManager mNotificationRoundnessManager;
-    @Mock
-    private TunerService mTunerService;
-    @Mock
-    private DynamicPrivacyController mDynamicPrivacyController;
-    @Mock
-    private ConfigurationController mConfigurationController;
-    @Mock
-    private NotificationStackScrollLayout mNotificationStackScrollLayout;
-    @Mock
-    private ZenModeController mZenModeController;
-    @Mock
-    private KeyguardMediaController mKeyguardMediaController;
-    @Mock
-    private SysuiStatusBarStateController mSysuiStatusBarStateController;
-    @Mock
-    private KeyguardBypassController mKeyguardBypassController;
-    @Mock
-    private SysuiColorExtractor mColorExtractor;
-    @Mock
-    private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
-    @Mock
-    private MetricsLogger mMetricsLogger;
-    @Mock
-    private FalsingManager mFalsingManager;
-    @Mock
-    private NotificationSectionsManager mNotificationSectionsManager;
-    @Mock
-    private Resources mResources;
+    @Mock private NotificationGutsManager mNotificationGutsManager;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
+    @Mock private TunerService mTunerService;
+    @Mock private DynamicPrivacyController mDynamicPrivacyController;
+    @Mock private ConfigurationController mConfigurationController;
+    @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
+    @Mock private ZenModeController mZenModeController;
+    @Mock private KeyguardMediaController mKeyguardMediaController;
+    @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
+    @Mock private KeyguardBypassController mKeyguardBypassController;
+    @Mock private SysuiColorExtractor mColorExtractor;
+    @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+    @Mock private MetricsLogger mMetricsLogger;
+    @Mock private FalsingManager mFalsingManager;
+    @Mock private NotificationSectionsManager mNotificationSectionsManager;
+    @Mock private Resources mResources;
     @Mock(answer = Answers.RETURNS_SELF)
     private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
-    @Mock
-    private NotificationSwipeHelper mNotificationSwipeHelper;
-    @Mock
-    private StatusBar mStatusBar;
+    @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
+    @Mock private StatusBar mStatusBar;
+    @Mock private ScrimController mScrimController;
+    @Mock private NotificationGroupManagerLegacy mLegacyGroupManager;
+    @Mock private SectionHeaderController mSilentHeaderController;
+
+    @Captor
+    ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
 
     private NotificationStackScrollLayoutController mController;
 
@@ -138,7 +132,11 @@
                 mNotificationSectionsManager,
                 mResources,
                 mNotificationSwipeHelperBuilder,
-                mStatusBar
+                mStatusBar,
+                mScrimController,
+                mLegacyGroupManager,
+                mLegacyGroupManager,
+                mSilentHeaderController
         );
 
         when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
@@ -176,32 +174,49 @@
     }
 
     @Test
-    public void testUpdateEmptyShadeView_notificationsVisible() {
+    public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
         mController.attach(mNotificationStackScrollLayout);
+        verify(mSysuiStatusBarStateController).addCallback(
+                mStateListenerArgumentCaptor.capture(), anyInt());
+        StatusBarStateController.StateListener stateListener =
+                mStateListenerArgumentCaptor.getValue();
 
-        mController.updateEmptyShadeView(true /* visible */);
+        setupShowEmptyShadeViewState(stateListener, true);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
         verify(mNotificationStackScrollLayout).updateEmptyShadeView(
                 true /* visible */,
+
                 true /* notifVisibleInShade */);
+
+        setupShowEmptyShadeViewState(stateListener, false);
         reset(mNotificationStackScrollLayout);
-        mController.updateEmptyShadeView(false /* visible */);
+        mController.updateShowEmptyShadeView();
         verify(mNotificationStackScrollLayout).updateEmptyShadeView(
                 false /* visible */,
                 true /* notifVisibleInShade */);
     }
 
     @Test
-    public void testUpdateEmptyShadeView_notificationsHidden() {
+    public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
         when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
         mController.attach(mNotificationStackScrollLayout);
+        verify(mSysuiStatusBarStateController).addCallback(
+                mStateListenerArgumentCaptor.capture(), anyInt());
+        StatusBarStateController.StateListener stateListener =
+                mStateListenerArgumentCaptor.getValue();
 
-        mController.updateEmptyShadeView(true /* visible */);
+        setupShowEmptyShadeViewState(stateListener, true);
+        reset(mNotificationStackScrollLayout);
+        mController.updateShowEmptyShadeView();
         verify(mNotificationStackScrollLayout).updateEmptyShadeView(
                 true /* visible */,
                 false /* notifVisibleInShade */);
+
+        setupShowEmptyShadeViewState(stateListener, false);
         reset(mNotificationStackScrollLayout);
-        mController.updateEmptyShadeView(false /* visible */);
+        mController.updateShowEmptyShadeView();
         verify(mNotificationStackScrollLayout).updateEmptyShadeView(
                 false /* visible */,
                 false /* notifVisibleInShade */);
@@ -229,15 +244,12 @@
     public void testOnStatePostChange_verifyIfProfileIsPublic() {
         when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
 
-        ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
-                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-
         mController.attach(mNotificationStackScrollLayout);
         verify(mSysuiStatusBarStateController).addCallback(
-                stateListenerArgumentCaptor.capture(), anyInt());
+                mStateListenerArgumentCaptor.capture(), anyInt());
 
         StatusBarStateController.StateListener stateListener =
-                stateListenerArgumentCaptor.getValue();
+                mStateListenerArgumentCaptor.getValue();
 
         stateListener.onStatePostChange();
         verify(mNotificationStackScrollLayout).updateSensitiveness(false, true);
@@ -294,6 +306,20 @@
         return argThat(new LogMatcher(category, type));
     }
 
+    private void setupShowEmptyShadeViewState(
+            StatusBarStateController.StateListener statusBarStateListener,
+            boolean toShow) {
+        if (toShow) {
+            statusBarStateListener.onStateChanged(SHADE);
+            mController.setQsExpanded(false);
+            mController.getView().removeAllViews();
+        } else {
+            statusBarStateListener.onStateChanged(KEYGUARD);
+            mController.setQsExpanded(true);
+            mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
+        }
+    }
+
     static class LogMatcher implements ArgumentMatcher<LogMaker> {
         private int mCategory, mType;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 2239b1b..57020eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -57,7 +58,7 @@
 
     private HeadsUpManagerPhone mHeadsUpManager;
 
-    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private NotificationGroupManagerLegacy mGroupManager;
     @Mock private View mNotificationShadeWindowView;
     @Mock private VisualStabilityManager mVSManager;
     @Mock private StatusBar mBar;
@@ -69,7 +70,7 @@
     private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
         TestableHeadsUpManagerPhone(
                 Context context,
-                NotificationGroupManager groupManager,
+                NotificationGroupManagerLegacy groupManager,
                 VisualStabilityManager vsManager,
                 StatusBarStateController statusBarStateController,
                 KeyguardBypassController keyguardBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 885dff3..2ece8be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
@@ -68,7 +69,7 @@
     @Rule public MockitoRule rule = MockitoJUnit.rule();
 
     private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
-    private NotificationGroupManager mGroupManager;
+    private NotificationGroupManagerLegacy mGroupManager;
     private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationEntryManager mNotificationEntryManager;
     @Mock private RowContentBindStage mBindStage;
@@ -88,10 +89,10 @@
         when(mNotificationEntryManager.getPendingNotificationsIterator())
                 .thenReturn(mPendingEntries.values());
 
-        mGroupManager = new NotificationGroupManager(
+        mGroupManager = new NotificationGroupManagerLegacy(
                 mock(StatusBarStateController.class),
                 () -> mock(PeopleNotificationIdentifier.class));
-        mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
+        mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
 
         when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 5a6f74a..0aa0091 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -47,11 +48,11 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class NotificationGroupManagerTest extends SysuiTestCase {
+public class NotificationGroupManagerLegacyTest extends SysuiTestCase {
     @Rule
     public MockitoRule rule = MockitoJUnit.rule();
 
-    private NotificationGroupManager mGroupManager;
+    private NotificationGroupManagerLegacy mGroupManager;
     private final NotificationGroupTestHelper mGroupTestHelper =
             new NotificationGroupTestHelper(mContext);
 
@@ -64,7 +65,7 @@
     }
 
     private void initializeGroupManager() {
-        mGroupManager = new NotificationGroupManager(
+        mGroupManager = new NotificationGroupManagerLegacy(
                 mock(StatusBarStateController.class),
                 () -> mock(PeopleNotificationIdentifier.class));
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
@@ -78,7 +79,7 @@
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        assertTrue(mGroupManager.isOnlyChildInGroup(childEntry.getSbn()));
+        assertTrue(mGroupManager.isOnlyChildInGroup(childEntry));
     }
 
     @Test
@@ -90,7 +91,7 @@
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
 
-        assertTrue(mGroupManager.isChildInGroupWithSummary(childEntry.getSbn()));
+        assertTrue(mGroupManager.isChildInGroup(childEntry));
     }
 
     @Test
@@ -102,8 +103,8 @@
         mGroupManager.onEntryAdded(childEntry);
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
 
-        assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.getSbn()));
-        assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.getSbn()));
+        assertTrue(mGroupManager.isGroupSummary(summaryEntry));
+        assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry));
     }
 
     @Test
@@ -116,7 +117,7 @@
 
         mGroupManager.onEntryRemoved(childEntry);
 
-        assertFalse(mGroupManager.isChildInGroupWithSummary(childEntry.getSbn()));
+        assertFalse(mGroupManager.isChildInGroup(childEntry));
     }
 
     @Test
@@ -129,8 +130,8 @@
 
         mGroupManager.onEntryRemoved(summaryEntry);
 
-        assertNull(mGroupManager.getGroupSummary(childEntry.getSbn()));
-        assertFalse(mGroupManager.isSummaryOfGroup(summaryEntry.getSbn()));
+        assertNull(mGroupManager.getGroupSummary(childEntry));
+        assertFalse(mGroupManager.isGroupSummary(summaryEntry));
     }
 
     @Test
@@ -146,7 +147,7 @@
 
         // Child entries that are heads upped should be considered separate groups visually even if
         // they are the same group logically
-        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.getSbn()));
-        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.getSbn()));
+        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index a9484af..7ee27c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -62,7 +62,6 @@
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShelfController;
@@ -74,13 +73,14 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -112,15 +112,13 @@
     @Mock
     private ViewGroup mBigClockContainer;
     @Mock
-    private ScrimController mScrimController;
-    @Mock
     private NotificationIconAreaController mNotificationAreaController;
     @Mock
     private HeadsUpManagerPhone mHeadsUpManager;
     @Mock
     private NotificationShelfController mNotificationShelfController;
     @Mock
-    private NotificationGroupManager mGroupManager;
+    private NotificationGroupManagerLegacy mGroupManager;
     @Mock
     private KeyguardStatusBarView mKeyguardStatusBar;
     @Mock
@@ -176,8 +174,6 @@
     private KeyguardClockSwitch mKeyguardClockSwitch;
     private PanelViewController.TouchHandler mTouchHandler;
     @Mock
-    private ZenModeController mZenModeController;
-    @Mock
     private ConfigurationController mConfigurationController;
     @Mock
     private MediaHierarchyManager mMediaHiearchyManager;
@@ -196,8 +192,6 @@
     @Mock
     private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
-    private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
-
     private NotificationPanelViewController mNotificationPanelViewController;
     private View.AccessibilityDelegate mAccessibiltyDelegate;
 
@@ -261,18 +255,17 @@
                 mKeyguardStateController, mStatusBarStateController, mDozeLog,
                 mDozeParameters, mCommandQueue, mVibratorHelper,
                 mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
-                mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController,
+                mMetricsLogger, mActivityManager, mConfigurationController,
                 flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
                 mConversationNotificationManager, mMediaHiearchyManager,
                 mBiometricUnlockController, mStatusBarKeyguardViewManager,
                 mNotificationStackScrollLayoutController,
-                mNotificationAreaController,
-                mKeyguardStatusViewComponentFactory);
+                mKeyguardStatusViewComponentFactory,
+                mGroupManager,
+                mNotificationAreaController);
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
-                mGroupManager,
-                mNotificationShelfController,
-                mScrimController);
+                mNotificationShelfController);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
         mNotificationPanelViewController.setBar(mPanelBar);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index c1d51f3..25af584 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.InjectionInflationController;
@@ -85,6 +86,7 @@
     @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
     @Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
     @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
+    @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
     @Before
     public void setUp() {
@@ -123,11 +125,11 @@
                 mNotificationShadeDepthController,
                 mView,
                 mNotificationPanelViewController,
-                mStatusBarViewFactory);
+                mStatusBarViewFactory,
+                mNotificationStackScrollLayoutController);
         mController.setupExpandedStatusBar();
         mController.setService(mStatusBar, mNotificationShadeWindowController);
         mController.setDragDownHelper(mDragDownHelper);
-
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 2f45113..1083273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -27,7 +28,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
@@ -39,14 +39,13 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -71,8 +70,6 @@
     @Mock
     private LockPatternUtils mLockPatternUtils;
     @Mock
-    private KeyguardBouncer mBouncer;
-    @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
     private StatusBar mStatusBar;
@@ -94,6 +91,13 @@
     private KeyguardBypassController mBypassController;
     @Mock
     private FaceAuthScreenBrightnessController mFaceAuthScreenBrightnessController;
+    @Mock
+    private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
+    @Mock
+    private KeyguardBouncerComponent mKeyguardBouncerComponent;
+    @Mock
+    private KeyguardBouncer mBouncer;
+
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     @Before
@@ -102,7 +106,14 @@
         when(mLockIconContainer.getParent()).thenReturn(mock(ViewGroup.class));
         when(mLockIconContainer.animate()).thenReturn(mock(ViewPropertyAnimator.class,
                 RETURNS_DEEP_STUBS));
-        mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager(
+
+        when(mKeyguardBouncerComponentFactory.build(
+                any(ViewGroup.class),
+                any(KeyguardBouncer.BouncerExpansionCallback.class)))
+                .thenReturn(mKeyguardBouncerComponent);
+        when(mKeyguardBouncerComponent.createKeyguardBouncer()).thenReturn(mBouncer);
+
+        mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
                 getContext(),
                 mViewMediatorCallback,
                 mLockPatternUtils,
@@ -113,12 +124,12 @@
                 mock(DockManager.class),
                 mock(NotificationShadeWindowController.class),
                 mKeyguardStateController,
-                mFaceAuthScreenBrightnessController,
-                mock(NotificationMediaManager.class));
+                Optional.of(mFaceAuthScreenBrightnessController),
+                mock(NotificationMediaManager.class),
+                mKeyguardBouncerComponentFactory);
         mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
-                mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry,
-                mLockIconContainer, mNotificationContainer, mBypassController,
-                new FalsingManagerFake());
+                mNotificationPanelView, mBiometrucUnlockController,
+                mLockIconContainer, mNotificationContainer, mBypassController);
         mStatusBarKeyguardViewManager.show(null);
     }
 
@@ -267,38 +278,4 @@
         verify(action).onDismiss();
         verify(cancelAction, never()).run();
     }
-
-    private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
-
-        public TestableStatusBarKeyguardViewManager(Context context,
-                ViewMediatorCallback callback,
-                LockPatternUtils lockPatternUtils,
-                SysuiStatusBarStateController sysuiStatusBarStateController,
-                ConfigurationController configurationController,
-                KeyguardUpdateMonitor keyguardUpdateMonitor,
-                NavigationModeController navigationModeController,
-                DockManager dockManager,
-                NotificationShadeWindowController notificationShadeWindowController,
-                KeyguardStateController keyguardStateController,
-                FaceAuthScreenBrightnessController faceAuthScreenBrightnessController,
-                NotificationMediaManager notificationMediaManager) {
-            super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
-                    configurationController, keyguardUpdateMonitor, navigationModeController,
-                    dockManager, notificationShadeWindowController, keyguardStateController,
-                    Optional.of(faceAuthScreenBrightnessController), notificationMediaManager);
-        }
-
-        @Override
-        public void registerStatusBar(StatusBar statusBar, ViewGroup container,
-                NotificationPanelViewController notificationPanelViewController,
-                BiometricUnlockController fingerprintUnlockController,
-                DismissCallbackRegistry dismissCallbackRegistry,
-                ViewGroup lockIconContainer, View notificationContainer,
-                KeyguardBypassController bypassController, FalsingManager falsingManager) {
-            super.registerStatusBar(statusBar, container, notificationPanelViewController,
-                    fingerprintUnlockController, dismissCallbackRegistry, lockIconContainer,
-                    notificationContainer, bypassController, falsingManager);
-            mBouncer = StatusBarKeyguardViewManagerTest.this.mBouncer;
-        }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 3f631b1..792637d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -67,9 +67,9 @@
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -122,8 +122,6 @@
     private FeatureFlags mFeatureFlags;
     @Mock
     private NotifPipeline mNotifPipeline;
-    @Mock
-    private NotifCollection mNotifCollection;
 
     @Mock
     private ActivityIntentHelper mActivityIntentHelper;
@@ -187,7 +185,6 @@
                         mUiBgExecutor,
                         mEntryManager,
                         mNotifPipeline,
-                        mNotifCollection,
                         mock(HeadsUpManagerPhone.class),
                         mActivityStarter,
                         mClickNotifier,
@@ -198,7 +195,7 @@
                         mBubbleController,
                         () -> mAssistManager,
                         mRemoteInputManager,
-                        mock(NotificationGroupManager.class),
+                        mock(NotificationGroupManagerLegacy.class),
                         mock(NotificationLockscreenUserManager.class),
                         mShadeController,
                         mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index bf2bd38..6fbbee2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -72,7 +73,7 @@
                 mNotificationLockscreenUserManager);
 
         mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
-                mock(NotificationGroupManager.class), mNotificationLockscreenUserManager,
+                mock(NotificationGroupManagerLegacy.class), mNotificationLockscreenUserManager,
                 mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
                 mActivityStarter, mShadeController, new CommandQueue(mContext),
                 mock(ActionClickLogger.class)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 87aee3f..7d8a626 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -96,7 +96,6 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
@@ -143,6 +142,7 @@
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.volume.VolumeComponent;
+import com.android.wm.shell.splitscreen.SplitScreen;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -220,7 +220,6 @@
     @Mock private NetworkController mNetworkController;
     @Mock private VibratorHelper mVibratorHelper;
     @Mock private BubbleController mBubbleController;
-    @Mock private NotificationGroupManager mGroupManager;
     @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
     @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@@ -376,7 +375,6 @@
                 mStatusBarStateController,
                 mVibratorHelper,
                 mBubbleController,
-                mGroupManager,
                 mVisualStabilityManager,
                 mDeviceProvisionedController,
                 mNavigationBarController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index d8aa29e..6899217 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -3,6 +3,8 @@
 import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -10,6 +12,7 @@
 import android.net.NetworkCapabilities;
 import android.os.Looper;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -259,6 +262,25 @@
         assertDataNetworkNameEquals(newDataName);
     }
 
+    @Test
+    public void testIsDataInService_true() {
+        setupDefaultSignal();
+        assertTrue(mNetworkController.isMobileDataNetworkInService());
+    }
+
+    @Test
+    public void testIsDataInService_noSignal_false() {
+        assertFalse(mNetworkController.isMobileDataNetworkInService());
+    }
+
+    @Test
+    public void testIsDataInService_notInService_false() {
+        setupDefaultSignal();
+        setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+        setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
+        assertFalse(mNetworkController.isMobileDataNetworkInService());
+    }
+
     private void testDataActivity(int direction, boolean in, boolean out) {
         updateDataActivity(direction);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
index 8cb5f3e..6976422 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -53,9 +53,9 @@
     }
 
     @Override
-    public void registerContentObserverForUser(String name, ContentObserver settingsObserver,
-            int userHandle) {
-        SettingsKey key = new SettingsKey(userHandle, name);
+    public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
+            ContentObserver settingsObserver, int userHandle) {
+        SettingsKey key = new SettingsKey(userHandle, uri.toString());
         mContentObservers.putIfAbsent(key, new ArrayList<>());
         List<ContentObserver> observers = mContentObservers.get(key);
         observers.add(settingsObserver);
@@ -86,7 +86,7 @@
 
     @Override
     public String getStringForUser(String name, int userHandle) {
-        return mValues.get(new SettingsKey(userHandle, name));
+        return mValues.get(new SettingsKey(userHandle, getUriFor(name).toString()));
     }
 
     @Override
@@ -107,7 +107,7 @@
     @Override
     public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
             int userHandle, boolean overrideableByRestore) {
-        SettingsKey key = new SettingsKey(userHandle, name);
+        SettingsKey key = new SettingsKey(userHandle, getUriFor(name).toString());
         mValues.put(key, value);
 
         Uri uri = getUriFor(name);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index d5ba381..e7acfae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -95,6 +95,11 @@
     }
 
     @Override
+    public boolean isMobileDataNetworkInService() {
+        return false;
+    }
+
+    @Override
     public int getNumberSubscriptions() {
         return 0;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
new file mode 100644
index 0000000..bcc3639
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 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.wmshell;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.pip.Pip;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.tracing.ProtoTracer;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedGestureHandler;
+import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WMShellTest extends SysuiTestCase {
+
+    WMShell mWMShell;
+    @Mock CommandQueue mCommandQueue;
+    @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock ActivityManagerWrapper mActivityManagerWrapper;
+    @Mock DisplayImeController mDisplayImeController;
+    @Mock NavigationModeController mNavigationModeController;
+    @Mock ScreenLifecycle mScreenLifecycle;
+    @Mock SysUiState mSysUiState;
+    @Mock Pip mPip;
+    @Mock SplitScreen mSplitScreen;
+    @Mock OneHanded mOneHanded;
+    @Mock ProtoTracer mProtoTracer;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mWMShell = new WMShell(mContext, mCommandQueue, mKeyguardUpdateMonitor,
+                mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
+                mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
+                Optional.of(mOneHanded), mProtoTracer);
+    }
+
+    @Test
+    public void start_startsMonitorDisplays() {
+        mWMShell.start();
+
+        verify(mDisplayImeController).startMonitorDisplays();
+    }
+
+    @Test
+    public void initPip_registersCommandQueueCallback() {
+        mWMShell.initPip(mPip);
+
+        verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
+    }
+
+    @Test
+    public void initSplitScreen_registersCallbacks() {
+        mWMShell.initSplitScreen(mSplitScreen);
+
+        verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+        verify(mActivityManagerWrapper).registerTaskStackListener(
+                any(TaskStackChangeListener.class));
+    }
+
+    @Test
+    public void initOneHanded_registersCallbacks() {
+        when(mOneHanded.hasOneHandedFeature()).thenReturn(true);
+        mWMShell.initOneHanded(mOneHanded);
+
+        verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+        verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
+        verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
+        verify(mNavigationModeController).addListener(
+                any(NavigationModeController.ModeChangedListener.class));
+        verify(mActivityManagerWrapper).registerTaskStackListener(
+                any(TaskStackChangeListener.class));
+
+        verify(mOneHanded).registerGestureCallback(any(
+                OneHandedGestureHandler.OneHandedGestureEventCallback.class));
+        verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
+    }
+}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index c8becce..bf643cd 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -16,19 +16,9 @@
 java_sdk_library {
     name: "framework-tethering",
     defaults: ["framework-module-defaults"],
+    impl_library_visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
 
-    // Allow access to the stubs from anywhere.
-    visibility: ["//visibility:public"],
-
-    // Restrict access to implementation library.
-    impl_library_visibility: [
-        "//visibility:override", // Ignore the visibility property.
-        "//frameworks/base/packages/Tethering:__subpackages__",
-    ],
-
-    srcs: [
-        ":framework-tethering-srcs",
-    ],
+    srcs: [":framework-tethering-srcs"],
 
     jarjar_rules: "jarjar-rules.txt",
     installable: true,
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index 070626be..df4a52e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -256,6 +256,7 @@
                 return actionMasked;
         }
     }
+
     /**
      * Sends down events to the view hierarchy for all pointers which are not already being
      * delivered i.e. pointers that are not yet injected.
@@ -285,6 +286,79 @@
     }
 
     /**
+     * Sends down events to the view hierarchy for all pointers which are not already being
+     * delivered with original down location. i.e. pointers that are not yet injected. The down time
+     * is also replaced by the original one.
+     *
+     *
+     * @param prototype The prototype from which to create the injected events.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    void sendDownForAllNotInjectedPointersWithOriginalDown(MotionEvent prototype, int policyFlags) {
+        // Inject the injected pointers.
+        int pointerIdBits = 0;
+        final int pointerCount = prototype.getPointerCount();
+        final MotionEvent event = computeInjectionDownEvent(prototype);
+        for (int i = 0; i < pointerCount; i++) {
+            final int pointerId = prototype.getPointerId(i);
+            // Do not send event for already delivered pointers.
+            if (!mState.isInjectedPointerDown(pointerId)) {
+                pointerIdBits |= (1 << pointerId);
+                final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
+                sendMotionEvent(
+                        event,
+                        action,
+                        mState.getLastReceivedEvent(),
+                        pointerIdBits,
+                        policyFlags);
+            }
+        }
+    }
+
+    private MotionEvent computeInjectionDownEvent(MotionEvent prototype) {
+        final int pointerCount = prototype.getPointerCount();
+        if (pointerCount != mState.getReceivedPointerTracker().getReceivedPointerDownCount()) {
+            Slog.w(LOG_TAG, "The pointer count doesn't match the received count.");
+            return MotionEvent.obtain(prototype);
+        }
+        MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount];
+        MotionEvent.PointerProperties[] properties =
+                new MotionEvent.PointerProperties[pointerCount];
+        for (int i = 0; i < pointerCount; ++i) {
+            final int pointerId = prototype.getPointerId(i);
+            final float x = mState.getReceivedPointerTracker().getReceivedPointerDownX(pointerId);
+            final float y = mState.getReceivedPointerTracker().getReceivedPointerDownY(pointerId);
+            coords[i] = new MotionEvent.PointerCoords();
+            coords[i].x = x;
+            coords[i].y = y;
+            properties[i] = new MotionEvent.PointerProperties();
+            properties[i].id = pointerId;
+            properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER;
+        }
+        MotionEvent event =
+                MotionEvent.obtain(
+                        prototype.getDownTime(),
+                        // The event time is used for downTime while sending ACTION_DOWN. We adjust
+                        // it to avoid the motion velocity is too fast in the beginning after
+                        // Delegating.
+                        prototype.getDownTime(),
+                        prototype.getAction(),
+                        pointerCount,
+                        properties,
+                        coords,
+                        prototype.getMetaState(),
+                        prototype.getButtonState(),
+                        prototype.getXPrecision(),
+                        prototype.getYPrecision(),
+                        prototype.getDeviceId(),
+                        prototype.getEdgeFlags(),
+                        prototype.getSource(),
+                        prototype.getFlags());
+        return event;
+    }
+
+    /**
+     *
      * Sends up events to the view hierarchy for all pointers which are already being delivered i.e.
      * pointers that are injected.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
index 07e8211..5b46cb4 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -294,7 +294,7 @@
                                 + Float.toString(mGestureDetectionThresholdPixels));
             }
             if (getState() == STATE_CLEAR) {
-                if (moveDelta < mTouchSlop) {
+                if (moveDelta < (mTargetFingerCount * mTouchSlop)) {
                     // This still counts as a touch not a swipe.
                     continue;
                 } else if (mStrokeBuffers[pointerIndex].size() == 0) {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index d127172..8305be3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -608,7 +608,7 @@
                                 mReceivedPointerTracker.getReceivedPointerDownY(id)
                                         - rawEvent.getY(index);
                         final double moveDelta = Math.hypot(deltaX, deltaY);
-                        if (moveDelta < mTouchSlop) {
+                        if (moveDelta < (2 * mTouchSlop)) {
                             return;
                         }
                     }
@@ -644,23 +644,20 @@
                 if (mGestureDetector.isMultiFingerGesturesEnabled()) {
                     if (mGestureDetector.isTwoFingerPassthroughEnabled()) {
                         if (event.getPointerCount() == 3) {
-                            boolean isOnBottomEdge = true;
                             // If three fingers went down on the bottom edge of the screen, delegate
                             // immediately.
-                            final long screenHeight =
-                                    mContext.getResources().getDisplayMetrics().heightPixels;
-                            for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) {
-                                if (mReceivedPointerTracker.getReceivedPointerDownY(i)
-                                        < (screenHeight - mEdgeSwipeHeightPixels)) {
-                                    isOnBottomEdge = false;
-                                }
-                            }
-                            if (isOnBottomEdge) {
+                            if (allPointersDownOnBottomEdge(event)) {
                                 if (DEBUG) {
                                     Slog.d(LOG_TAG, "Three-finger edge swipe detected.");
                                 }
                                 mState.startDelegating();
-                                mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
+                                if (mState.isTouchExploring()) {
+                                    mDispatcher.sendDownForAllNotInjectedPointers(event,
+                                            policyFlags);
+                                } else {
+                                    mDispatcher.sendDownForAllNotInjectedPointersWithOriginalDown(
+                                            event, policyFlags);
+                                }
                             }
                         }
                     }
@@ -1063,6 +1060,22 @@
         return downEvent;
     }
 
+    private boolean allPointersDownOnBottomEdge(MotionEvent event) {
+        final long screenHeight =
+                mContext.getResources().getDisplayMetrics().heightPixels;
+        for (int i = 0; i < event.getPointerCount(); ++i) {
+            final int pointerId = event.getPointerId(i);
+            final float pointerDownY = mReceivedPointerTracker.getReceivedPointerDownY(pointerId);
+            if (pointerDownY < (screenHeight - mEdgeSwipeHeightPixels)) {
+                if (DEBUG) {
+                    Slog.d(LOG_TAG, "The pointer is not on the bottom edge" + pointerDownY);
+                }
+                return false;
+            }
+        }
+        return true;
+    }
+
     public TouchState getState() {
         return mState;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index b6f2a47..c583dcc 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -37,6 +37,7 @@
 import android.view.Display;
 import android.view.MagnificationSpec;
 import android.view.View;
+import android.view.accessibility.MagnificationAnimationCallback;
 import android.view.animation.DecelerateInterpolator;
 
 import com.android.internal.R;
@@ -63,7 +64,7 @@
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "FullScreenMagnificationController";
 
-    private static final Runnable STUB_RUNNABLE = () -> {
+    private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
     };
     public static final float MIN_SCALE = 1.0f;
     public static final float MAX_SCALE = 8.0f;
@@ -304,18 +305,19 @@
             }
         }
 
-        void sendSpecToAnimation(MagnificationSpec spec, Runnable endCallback) {
+        void sendSpecToAnimation(MagnificationSpec spec,
+                MagnificationAnimationCallback animationCallback) {
             if (DEBUG) {
                 Slog.i(LOG_TAG,
-                        "sendSpecToAnimation(spec = " + spec + ", endCallback = " + endCallback
-                                + ")");
+                        "sendSpecToAnimation(spec = " + spec + ", animationCallback = "
+                                + animationCallback + ")");
             }
             if (Thread.currentThread().getId() == mMainThreadId) {
-                mSpecAnimationBridge.updateSentSpecMainThread(spec, endCallback);
+                mSpecAnimationBridge.updateSentSpecMainThread(spec, animationCallback);
             } else {
                 final Message m = PooledLambda.obtainMessage(
                         SpecAnimationBridge::updateSentSpecMainThread,
-                        mSpecAnimationBridge, spec, endCallback);
+                        mSpecAnimationBridge, spec, animationCallback);
                 mControllerCtx.getHandler().sendMessage(m);
             }
         }
@@ -415,11 +417,11 @@
 
         @GuardedBy("mLock")
         boolean reset(boolean animate) {
-            return reset(transformToStubRunnable(animate));
+            return reset(transformToStubCallback(animate));
         }
 
         @GuardedBy("mLock")
-        boolean reset(Runnable endCallback) {
+        boolean reset(MagnificationAnimationCallback animationCallback) {
             if (!mRegistered) {
                 return false;
             }
@@ -430,7 +432,7 @@
                 onMagnificationChangedLocked();
             }
             mIdOfLastServiceToMagnify = INVALID_ID;
-            sendSpecToAnimation(spec, endCallback);
+            sendSpecToAnimation(spec, animationCallback);
             return changed;
         }
 
@@ -458,24 +460,23 @@
             final float centerX = normPivotX + offsetX;
             final float centerY = normPivotY + offsetY;
             mIdOfLastServiceToMagnify = id;
-            return setScaleAndCenter(scale, centerX, centerY, transformToStubRunnable(animate), id);
+            return setScaleAndCenter(scale, centerX, centerY, transformToStubCallback(animate), id);
         }
 
         @GuardedBy("mLock")
         boolean setScaleAndCenter(float scale, float centerX, float centerY,
-                Runnable endCallback, int id) {
+                MagnificationAnimationCallback animationCallback, int id) {
             if (!mRegistered) {
                 return false;
             }
             if (DEBUG) {
                 Slog.i(LOG_TAG,
                         "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
-                                + ", centerY = " + centerY + ", endCallback = " + endCallback
-                                + ", id = " + id
-                                + ")");
+                                + ", centerY = " + centerY + ", endCallback = "
+                                + animationCallback + ", id = " + id + ")");
             }
             final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
-            sendSpecToAnimation(mCurrentMagnificationSpec, endCallback);
+            sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
             if (isMagnifying() && (id != INVALID_ID)) {
                 mIdOfLastServiceToMagnify = id;
             }
@@ -875,7 +876,7 @@
      *         the spec did not change
      */
     public boolean reset(int displayId, boolean animate) {
-        return reset(displayId, animate ?  STUB_RUNNABLE : null);
+        return reset(displayId, animate ? STUB_ANIMATION_CALLBACK : null);
     }
 
     /**
@@ -883,18 +884,19 @@
      * transition.
      *
      * @param displayId The logical display id.
-     * @param endCallback Called when the animation is ended or the spec did not change.
+     * @param animationCallback Called when the animation result is valid.
      *                    {@code null} to transition immediately
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
-    public boolean reset(int displayId, Runnable endCallback) {
+    public boolean reset(int displayId,
+            MagnificationAnimationCallback animationCallback) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.reset(endCallback);
+            return display.reset(animationCallback);
         }
     }
 
@@ -946,7 +948,7 @@
                 return false;
             }
             return display.setScaleAndCenter(Float.NaN, centerX, centerY,
-                    animate ?  STUB_RUNNABLE : null, id);
+                    animate ? STUB_ANIMATION_CALLBACK : null, id);
         }
     }
 
@@ -970,7 +972,7 @@
     public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
             boolean animate, int id) {
         return setScaleAndCenter(displayId, scale, centerX, centerY,
-                transformToStubRunnable(animate), id);
+                transformToStubCallback(animate), id);
     }
 
     /**
@@ -984,20 +986,20 @@
      *                center and scale, or {@link Float#NaN} to leave unchanged
      * @param centerY the screen-relative Y coordinate around which to
      *                center and scale, or {@link Float#NaN} to leave unchanged
-     * @param endCallback called when the transition is finished successfully or the spec did not
-     *                   change. {@code null} to transition immediately.
+     * @param animationCallback Called when the animation result is valid.
+     *                           {@code null} to transition immediately
      * @param id the ID of the service requesting the change
      * @return {@code true} if the magnification spec changed, {@code false} if
      *         the spec did not change
      */
     public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
-            Runnable endCallback, int id) {
+            MagnificationAnimationCallback animationCallback, int id) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.setScaleAndCenter(scale, centerX, centerY, endCallback, id);
+            return display.setScaleAndCenter(scale, centerX, centerY, animationCallback, id);
         }
     }
 
@@ -1230,7 +1232,7 @@
         private final ValueAnimator mValueAnimator;
 
         // Called when the callee wants animating and the sent spec matches the target spec.
-        private Runnable mEndCallback;
+        private MagnificationAnimationCallback mAnimationCallback;
         private final Object mLock;
 
         private final int mDisplayId;
@@ -1268,33 +1270,35 @@
             }
         }
 
-        void updateSentSpecMainThread(MagnificationSpec spec, Runnable endCallback) {
+        void updateSentSpecMainThread(MagnificationSpec spec,
+                MagnificationAnimationCallback animationCallback) {
             if (mValueAnimator.isRunning()) {
-                // Avoid AnimationEnd Callback.
-                mEndCallback = null;
                 mValueAnimator.cancel();
             }
 
-            mEndCallback = endCallback;
+            mAnimationCallback = animationCallback;
             // If the current and sent specs don't match, update the sent spec.
             synchronized (mLock) {
                 final boolean changed = !mSentMagnificationSpec.equals(spec);
                 if (changed) {
-                    if (mEndCallback != null) {
+                    if (mAnimationCallback != null) {
                         animateMagnificationSpecLocked(spec);
                     } else {
                         setMagnificationSpecLocked(spec);
                     }
                 } else {
-                    sendEndCallbackMainThread();
+                    sendEndCallbackMainThread(true);
                 }
             }
         }
 
-        private void sendEndCallbackMainThread() {
-            if (mEndCallback != null) {
-                mEndCallback.run();
-                mEndCallback = null;
+        private void sendEndCallbackMainThread(boolean success) {
+            if (mAnimationCallback != null) {
+                if (DEBUG) {
+                    Slog.d(LOG_TAG, "sendEndCallbackMainThread: " + success);
+                }
+                mAnimationCallback.onResult(success);
+                mAnimationCallback = null;
             }
         }
 
@@ -1337,17 +1341,16 @@
 
         @Override
         public void onAnimationStart(Animator animation) {
-
         }
 
         @Override
         public void onAnimationEnd(Animator animation) {
-            sendEndCallbackMainThread();
+            sendEndCallbackMainThread(true);
         }
 
         @Override
         public void onAnimationCancel(Animator animation) {
-
+            sendEndCallbackMainThread(false);
         }
 
         @Override
@@ -1481,7 +1484,7 @@
     }
 
     @Nullable
-    private static Runnable transformToStubRunnable(boolean animate) {
-        return animate ? STUB_RUNNABLE : null;
+    private static MagnificationAnimationCallback transformToStubCallback(boolean animate) {
+        return animate ? STUB_ANIMATION_CALLBACK : null;
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index 49a8586..a401bcd3 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -19,6 +19,8 @@
 import static android.os.IBinder.DeathRecipient;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.accessibility.IWindowMagnificationConnection;
@@ -47,9 +49,10 @@
         mConnection.asBinder().linkToDeath(deathRecipient, 0);
     }
 
-    boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
+    boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+            @Nullable RemoteCallback endCallback) {
         try {
-            mConnection.enableWindowMagnification(displayId, scale, centerX, centerY);
+            mConnection.enableWindowMagnification(displayId, scale, centerX, centerY, endCallback);
         } catch (RemoteException e) {
             if (DBG) {
                 Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -71,9 +74,9 @@
         return true;
     }
 
-    boolean disableWindowMagnification(int displayId) {
+    boolean disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) {
         try {
-            mConnection.disableWindowMagnification(displayId);
+            mConnection.disableWindowMagnification(displayId, endCallback);
         } catch (RemoteException e) {
             if (DBG) {
                 Slog.e(TAG, "Error calling disableWindowMagnification()", e);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 3ee5b28..7d6067c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -176,7 +176,7 @@
             Slog.i(LOG_TAG, "onDestroy(); delayed = "
                     + mDetectingState.toString());
         }
-        mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, true);
+        mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
         resetToDetectState();
     }
 
@@ -211,14 +211,14 @@
         final float scale = MathUtils.constrain(
                 mWindowMagnificationMgr.getPersistedScale(),
                 MIN_SCALE, MAX_SCALE);
-        mWindowMagnificationMgr.enableWindowMagnifier(mDisplayId, scale, centerX, centerY);
+        mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY);
     }
 
     private void disableWindowMagnifier() {
         if (DEBUG_ALL) {
             Slog.i(LOG_TAG, "disableWindowMagnifier()");
         }
-        mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, false);
+        mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
     }
 
     private void toggleMagnification(float centerX, float centerY) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index ed2b26f..d3d56d7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -24,6 +24,7 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.MathUtils;
@@ -73,7 +74,7 @@
         public void onReceive(Context context, Intent intent) {
             final int displayId = context.getDisplayId();
             removeMagnificationButton(displayId);
-            disableWindowMagnification(displayId);
+            disableWindowMagnification(displayId, false);
         }
     };
 
@@ -84,6 +85,7 @@
 
     /**
      * Sets {@link IWindowMagnificationConnection}.
+     *
      * @param connection {@link IWindowMagnificationConnection}
      */
     public void setConnection(@Nullable IWindowMagnificationConnection connection) {
@@ -124,7 +126,6 @@
     }
 
     /**
-     *
      * @return {@code true} if {@link IWindowMagnificationConnection} is available
      */
     public boolean isConnected() {
@@ -136,10 +137,10 @@
     /**
      * Requests {@link IWindowMagnificationConnection} through
      * {@link StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)} and
-     * destroys all window magnifiers if necessary.
+     * destroys all window magnifications if necessary.
      *
      * @param connect {@code true} if needs connection, otherwise set the connection to null and
-     *                            destroy all window magnifiers.
+     *                destroy all window magnifications.
      * @return {@code true} if {@link IWindowMagnificationConnection} state is going to change.
      */
     public boolean requestConnection(boolean connect) {
@@ -171,7 +172,7 @@
     private void disableAllWindowMagnifiers() {
         for (int i = 0; i < mWindowMagnifiers.size(); i++) {
             final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
-            magnifier.disable();
+            magnifier.disableWindowMagnificationInternal(null);
         }
         mWindowMagnifiers.clear();
     }
@@ -187,12 +188,12 @@
 
     @Override
     public boolean processScroll(int displayId, float distanceX, float distanceY) {
-        moveWindowMagnifier(displayId, -distanceX, -distanceY);
+        moveWindowMagnification(displayId, -distanceX, -distanceY);
         return /* event consumed: */ true;
     }
 
     /**
-     * Scales the magnified region on the specified display if the window magnifier is enabled.
+     * Scales the magnified region on the specified display if window magnification is initiated.
      *
      * @param displayId The logical display id.
      * @param scale The target scale, must be >= 1
@@ -209,37 +210,69 @@
     }
 
     /**
-     * Enables the window magnifier with specified center and scale on the specified display.
-     *  @param displayId The logical display id.
+     * Enables window magnification with specified center and scale on the given display and
+     * animating the transition.
+     *
+     * @param displayId The logical display id.
      * @param scale The target scale, must be >= 1.
      * @param centerX The screen-relative X coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
      * @param centerY The screen-relative Y coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
      */
-    void enableWindowMagnifier(int displayId, float scale, float centerX, float centerY) {
+    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
+        enableWindowMagnification(displayId, scale, centerX, centerY, null);
+    }
+
+    /**
+     * Enables window magnification with specified center and scale on the specified display and
+     * animating the transition.
+     *
+     * @param displayId The logical display id.
+     * @param scale The target scale, must be >= 1.
+     * @param centerX The screen-relative X coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     * @param centerY The screen-relative Y coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     * @param endCallback Called when the animation is ended without any interruption or the
+     *                    window magnifier is disabled already.
+     */
+    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
+            @Nullable Runnable endCallback) {
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null) {
                 magnifier = createWindowMagnifier(displayId);
             }
-            magnifier.enable(scale, centerX, centerY);
+            magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, endCallback);
         }
     }
 
     /**
-     * Disables the window magnifier on the specified display.
+     * Disables window magnification on the given display.
      *
      * @param displayId The logical display id.
-     * @param clear {@true} Clears the state of the window magnifier
+     * @param clear {@true} Clears the state of window magnification.
      */
-    void disableWindowMagnifier(int displayId, boolean clear) {
+    void disableWindowMagnification(int displayId, boolean clear) {
+        disableWindowMagnification(displayId, clear, null);
+    }
+
+    /**
+     * Disables window magnification on the specified display and animating the transition.
+     *
+     * @param displayId The logical display id.
+     * @param clear {@true} Clears the state of window magnification.
+     * @param endCallback Called when the animation is ended without any interruption or the
+     *                    window magnifier is disabled already.
+     */
+    void disableWindowMagnification(int displayId, boolean clear, Runnable endCallback) {
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null) {
                 return;
             }
-            magnifier.disable();
+            magnifier.disableWindowMagnificationInternal(endCallback);
             if (clear) {
                 mWindowMagnifiers.delete(displayId);
             }
@@ -264,10 +297,10 @@
     }
 
     /**
-     * Indicates whether this window magnifier is enabled on specified display.
+     * Indicates whether window magnification is enabled on specified display.
      *
      * @param displayId The logical display id.
-     * @return {@code true} if the window magnifier is enabled.
+     * @return {@code true} if the window magnification is enabled.
      */
     boolean isWindowMagnifierEnabled(int displayId) {
         synchronized (mLock) {
@@ -323,7 +356,7 @@
     }
 
     /**
-     * Moves the window magnifier with specified offset.
+     * Moves window magnification on the specified display with the specified offset.
      *
      * @param displayId The logical display id.
      * @param offsetX the amount in pixels to offset the region in the X direction, in current
@@ -331,7 +364,7 @@
      * @param offsetY the amount in pixels to offset the region in the Y direction, in current
      *                screen pixels.
      */
-    void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+    void moveWindowMagnification(int displayId, float offsetX, float offsetY) {
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null) {
@@ -366,6 +399,7 @@
 
     /**
      * Creates the windowMagnifier based on the specified display and stores it.
+     *
      * @param displayId logical display id.
      */
     @GuardedBy("mLock")
@@ -425,7 +459,8 @@
     }
 
     /**
-     * A class to  manipulate the window magnifier and contains the relevant information.
+     * A class manipulates window magnification per display and contains the magnification
+     * information.
      */
     private static class WindowMagnifier {
 
@@ -434,31 +469,34 @@
         private boolean mEnabled;
 
         private final WindowMagnificationManager mWindowMagnificationManager;
-        //Records the bounds of window magnifier.
+        //Records the bounds of window magnification.
         private final Rect mBounds = new Rect();
         //The magnified bounds on the screen.
         private final Rect mSourceBounds = new Rect();
+
         WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
             mDisplayId = displayId;
             mWindowMagnificationManager = windowMagnificationManager;
         }
 
         @GuardedBy("mLock")
-        void enable(float scale, float centerX, float centerY) {
+        void enableWindowMagnificationInternal(float scale, float centerX, float centerY,
+                @Nullable Runnable endCallback) {
             if (mEnabled) {
                 return;
             }
             final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-            if (mWindowMagnificationManager.enableWindowMagnification(mDisplayId, normScale,
-                    centerX, centerY)) {
+            if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
+                    centerX, centerY, endCallback)) {
                 mScale = normScale;
                 mEnabled = true;
             }
         }
 
         @GuardedBy("mLock")
-        void disable() {
-            if (mEnabled && mWindowMagnificationManager.disableWindowMagnification(mDisplayId)) {
+        void disableWindowMagnificationInternal(@Nullable Runnable endCallback) {
+            if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal(
+                    mDisplayId, endCallback)) {
                 mEnabled = false;
             }
         }
@@ -519,19 +557,21 @@
         }
     }
 
-    private boolean enableWindowMagnification(int displayId, float scale, float centerX,
-            float centerY) {
+    private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
+            float centerY, Runnable endCallback) {
         return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
-                displayId, scale, centerX, centerY);
+                displayId, scale, centerX, centerY,
+                endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
     }
 
     private boolean setScaleInternal(int displayId, float scale) {
         return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
     }
 
-    private boolean disableWindowMagnification(int displayId) {
+    private boolean disableWindowMagnificationInternal(int displayId, Runnable endCallback) {
         return mConnectionWrapper != null && mConnectionWrapper.disableWindowMagnification(
-                displayId);
+                displayId,
+                endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null);
     }
 
     private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) {
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b962539..16077cb 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -228,7 +228,7 @@
                             PackageManagerInternal.class);
                     RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
                             mBackupManagerService.getPackageManager(), allowApks, info, signatures,
-                            pmi, mUserId);
+                            pmi, mUserId, mBackupEligibilityRules);
                     mManifestSignatures.put(info.packageName, signatures);
                     mPackagePolicies.put(pkg, restorePolicy);
                     mPackageInstallers.put(pkg, info.installerPackageName);
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 3789fa1..6963248 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -389,13 +389,29 @@
     public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
             boolean allowApks, FileMetadata info, Signature[] signatures,
             PackageManagerInternal pmi, int userId) {
+        return chooseRestorePolicy(packageManager, allowApks, info, signatures, pmi, userId,
+                BackupEligibilityRules.forBackup(packageManager, pmi, userId));
+    }
+
+    /**
+     * Chooses restore policy.
+     *
+     * @param packageManager - PackageManager instance.
+     * @param allowApks - allow restore set to include apks.
+     * @param info - file metadata.
+     * @param signatures - array of signatures parsed from backup file.
+     * @param userId - ID of the user for which restore is performed.
+     * @param eligibilityRules - {@link BackupEligibilityRules} for this operation.
+     * @return a restore policy constant.
+     */
+    public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
+            boolean allowApks, FileMetadata info, Signature[] signatures,
+            PackageManagerInternal pmi, int userId, BackupEligibilityRules eligibilityRules) {
         if (signatures == null) {
             return RestorePolicy.IGNORE;
         }
 
         RestorePolicy policy = RestorePolicy.IGNORE;
-        BackupEligibilityRules eligibilityRules = BackupEligibilityRules.forBackup(packageManager,
-                pmi, userId);
         // Okay, got the manifest info we need...
         try {
             PackageInfo pkgInfo = packageManager.getPackageInfoAsUser(
diff --git a/services/companion/TEST_MAPPING b/services/companion/TEST_MAPPING
new file mode 100644
index 0000000..63f54fa
--- /dev/null
+++ b/services/companion/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "include-filter": "android.os.cts.CompanionDeviceManagerTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index eb38f51..7b9728cb 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -393,12 +393,14 @@
                             .toString());
             long identity = Binder.clearCallingIdentity();
             try {
-                return PendingIntent.getActivity(getContext(),
+                return PendingIntent.getActivityAsUser(getContext(),
                         0 /* request code */,
                         NotificationAccessConfirmationActivityContract.launcherIntent(
                                 userId, component, packageTitle),
                         PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT
-                                | PendingIntent.FLAG_CANCEL_CURRENT);
+                                | PendingIntent.FLAG_CANCEL_CURRENT,
+                        null /* options */,
+                        new UserHandle(userId));
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -612,8 +614,8 @@
         }
     }
 
-    private AtomicFile getStorageFileForUser(int uid) {
-        return mUidToStorage.computeIfAbsent(uid, (u) ->
+    private AtomicFile getStorageFileForUser(int userId) {
+        return mUidToStorage.computeIfAbsent(userId, (u) ->
                 new AtomicFile(new File(
                         //TODO deprecated method - what's the right replacement?
                         Environment.getUserSystemDirectory(u),
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index f372c6f..0d79240 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -166,6 +166,7 @@
     private String mAddress;
     private String mName;
     private final ContentResolver mContentResolver;
+    private final int mUserId;
     private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
     private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
     private IBinder mBluetoothBinder;
@@ -481,6 +482,7 @@
         mName = null;
         mErrorRecoveryRetryCounter = 0;
         mContentResolver = context.getContentResolver();
+        mUserId = mContentResolver.getUserId();
         // Observe BLE scan only mode settings change.
         registerForBleScanModeChange();
         mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
@@ -625,7 +627,8 @@
         }
         if (mContext.getResources()
                 .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
-                && Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0)
+                && Settings.Secure.getIntForUser(mContentResolver,
+                SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0, mUserId)
                 == 0) {
             // if the valid flag is not set, don't load the address and name
             if (DBG) {
@@ -633,8 +636,10 @@
             }
             return;
         }
-        mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
-        mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
+        mName = Settings.Secure.getStringForUser(
+                mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, mUserId);
+        mAddress = Settings.Secure.getStringForUser(
+                mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, mUserId);
         if (DBG) {
             Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
         }
@@ -648,26 +653,31 @@
      */
     private void storeNameAndAddress(String name, String address) {
         if (name != null) {
-            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
+            Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name,
+                    mUserId);
             mName = name;
             if (DBG) {
-                Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getString(mContentResolver,
-                        SECURE_SETTINGS_BLUETOOTH_NAME));
+                Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser(
+                        mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME,
+                        mUserId));
             }
         }
 
         if (address != null) {
-            Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
+            Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+                    address, mUserId);
             mAddress = address;
             if (DBG) {
                 Slog.d(TAG,
-                        "Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver,
-                                SECURE_SETTINGS_BLUETOOTH_ADDRESS));
+                        "Stored Bluetoothaddress: " + Settings.Secure.getStringForUser(
+                                mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+                                mUserId));
             }
         }
 
         if ((name != null) && (address != null)) {
-            Settings.Secure.putInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1);
+            Settings.Secure.putIntForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1,
+                    mUserId);
         }
     }
 
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index b09b260..500e768 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -126,6 +126,16 @@
     }
 
     @Override
+    public boolean closePartition() throws RemoteException {
+        IGsiService service = getGsiService();
+        if (service.closePartition() != 0) {
+            Slog.i(TAG, "Partition installation completes with error");
+            return false;
+        }
+        return true;
+    }
+
+    @Override
     public boolean finishInstallation() throws RemoteException {
         IGsiService service = getGsiService();
         if (service.closeInstall() != 0) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index eb8308b..e433fbd 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -64,6 +64,7 @@
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
 import android.telephony.PreciseDisconnectCause;
@@ -142,13 +143,13 @@
         int callerUid;
         int callerPid;
 
-        int events;
+        long events;
 
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
         int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 
-        boolean matchPhoneStateListenerEvent(int events) {
+        boolean matchPhoneStateListenerEvent(long events) {
             return (callback != null) && ((events & this.events) != 0);
         }
 
@@ -177,7 +178,7 @@
                     + onSubscriptionsChangedListenerCallback
                     + " onOpportunisticSubscriptionsChangedListenererCallback="
                     + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
-                    + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + "}";
+                    + " phoneId=" + phoneId + " events=" + Long.toHexString(events) + "}";
         }
     }
 
@@ -306,6 +307,8 @@
 
     private final LocalLog mListenLog = new LocalLog(00);
 
+    private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
      * type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
@@ -318,19 +321,19 @@
     // Starting in Q, almost all cellular location requires FINE location enforcement.
     // Prior to Q, cellular was available with COARSE location enforcement. Bits in this
     // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later.
-    static final int ENFORCE_LOCATION_PERMISSION_MASK =
+    static final long ENFORCE_LOCATION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_CELL_LOCATION
                     | PhoneStateListener.LISTEN_CELL_INFO
                     | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
                     | PhoneStateListener.LISTEN_BARRING_INFO;
 
-    static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
+    static final long ENFORCE_PHONE_STATE_PERMISSION_MASK =
             PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
                     | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
                     | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
                     | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
 
-    static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
+    static final long ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
             PhoneStateListener.LISTEN_PRECISE_CALL_STATE
                     | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
                     | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
@@ -339,11 +342,11 @@
                     | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
                     | PhoneStateListener.LISTEN_BARRING_INFO;
 
-    static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
+    static final long READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
                     | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS;
 
-    static final int READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK =
+    static final long READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT
                     | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED
                     | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED
@@ -495,6 +498,7 @@
             cutListToSize(mImsReasonInfo, mNumPhones);
             cutListToSize(mPreciseDataConnectionStates, mNumPhones);
             cutListToSize(mBarringInfo, mNumPhones);
+            cutListToSize(mPhysicalChannelConfigs, mNumPhones);
             return;
         }
 
@@ -528,6 +532,8 @@
             mPreciseDataConnectionStates.add(new ArrayMap<>());
             mBarringInfo.add(i, new BarringInfo());
             mTelephonyDisplayInfos[i] = null;
+            mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig(
+                    PhysicalChannelConfig.CONNECTION_UNKNOWN,0));
         }
     }
 
@@ -588,6 +594,7 @@
         mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
         mBarringInfo = new ArrayList<>();
         mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
+        mPhysicalChannelConfigs = new ArrayList<>();
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -617,6 +624,8 @@
             mPreciseDataConnectionStates.add(new ArrayMap<>());
             mBarringInfo.add(i, new BarringInfo());
             mTelephonyDisplayInfos[i] = null;
+            mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig(
+                    PhysicalChannelConfig.CONNECTION_UNKNOWN,0));
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -795,23 +804,23 @@
 
     @Override
     public void listenWithFeature(String callingPackage, String callingFeatureId,
-            IPhoneStateListener callback, int events, boolean notifyNow) {
+            IPhoneStateListener callback, long events, boolean notifyNow) {
         listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage,
                 callingFeatureId, callback, events, notifyNow);
     }
 
     @Override
     public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId,
-            IPhoneStateListener callback, int events, boolean notifyNow) {
+            IPhoneStateListener callback, long events, boolean notifyNow) {
         listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId);
     }
 
     private void listen(String callingPackage, @Nullable String callingFeatureId,
-            IPhoneStateListener callback, int events, boolean notifyNow, int subId) {
+            IPhoneStateListener callback, long events, boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
-                + " events=0x" + Integer.toHexString(events) + " notifyNow=" + notifyNow + " subId="
+                + " events=0x" + Long.toHexString(events) + " notifyNow=" + notifyNow + " subId="
                 + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId;
         mListenLog.log(str);
         if (VDBG) {
@@ -1098,6 +1107,14 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) {
+                        try {
+                            r.callback.onPhysicalChannelConfigurationChanged(
+                                    mPhysicalChannelConfigs);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -2258,6 +2275,47 @@
         }
     }
 
+    /**
+     * Send a notification to registrants that the configs of physical channel has changed for
+     * a particular subscription.
+     *
+     * @param subId the subId
+     * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
+     */
+    public void notifyPhysicalChannelConfigurationForSubscriber(
+            int subId, List<PhysicalChannelConfig> configs) {
+        if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
+            return;
+        }
+
+        if (VDBG) {
+            log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs);
+        }
+
+        synchronized (mRecords) {
+            int phoneId = SubscriptionManager.getPhoneId(subId);
+            if (validatePhoneId(phoneId)) {
+                mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId));
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION)
+                            && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            if (DBG_LOC) {
+                                log("notifyPhysicalChannelConfiguration: "
+                                        + "mPhysicalChannelConfigs="
+                                        + configs + " r=" + r);
+                            }
+                            r.callback.onPhysicalChannelConfigurationChanged(configs);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -2310,6 +2368,7 @@
             pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
             pw.println("mDefaultPhoneId=" + mDefaultPhoneId);
             pw.println("mDefaultSubId=" + mDefaultSubId);
+            pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs);
 
             pw.decreaseIndent();
 
@@ -2536,7 +2595,7 @@
                 == PackageManager.PERMISSION_GRANTED;
     }
 
-    private boolean checkListenerPermission(int events, int subId, String callingPackage,
+    private boolean checkListenerPermission(long events, int subId, String callingPackage,
             @Nullable String callingFeatureId, String message) {
         LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
@@ -2742,7 +2801,7 @@
     }
 
     private void checkPossibleMissNotify(Record r, int phoneId) {
-        int events = r.events;
+        long events = r.events;
 
         if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
             try {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6dbb1e92..bcb7bfa 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -489,6 +489,9 @@
      * @return True if the new value is different from the old value. False otherwise.
      */
     private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) {
+        if (mCarModeEnabled || mCar) {
+            return false;
+        }
         int oldNightMode = mNightMode;
         if (mSetupWizardComplete) {
             mNightMode = Secure.getIntForUser(context.getContentResolver(),
@@ -1033,7 +1036,7 @@
 
     private void persistNightMode(int user) {
         // Only persist setting if not in car mode
-        if (mCarModeEnabled) return;
+        if (mCarModeEnabled || mCar) return;
         Secure.putIntForUser(getContext().getContentResolver(),
                 Secure.UI_NIGHT_MODE, mNightMode, user);
         Secure.putLongForUser(getContext().getContentResolver(),
@@ -1046,7 +1049,7 @@
 
     private void persistNightModeOverrides(int user) {
         // Only persist setting if not in car mode
-        if (mCarModeEnabled) return;
+        if (mCarModeEnabled || mCar) return;
         Secure.putIntForUser(getContext().getContentResolver(),
                 Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user);
         Secure.putIntForUser(getContext().getContentResolver(),
@@ -1097,7 +1100,7 @@
         }
 
         // Override night mode in power save mode if not in car mode
-        if (mPowerSave && !mCarModeEnabled) {
+        if (mPowerSave && !mCarModeEnabled && !mCar) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
             uiMode |= Configuration.UI_MODE_NIGHT_YES;
         } else {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 35e88eb..7d81d41 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2114,7 +2114,7 @@
                  * Owner or system user account was renamed, rename the account for
                  * those users with which the account was shared.
                  */
-                    List<UserInfo> users = getUserManager().getUsers(true);
+                    List<UserInfo> users = getUserManager().getAliveUsers();
                     for (UserInfo user : users) {
                         if (user.isRestricted()
                                 && (user.restrictedProfileParentId == parentUserId)) {
@@ -2373,7 +2373,7 @@
             int parentUserId = accounts.userId;
             if (canHaveProfile(parentUserId)) {
                 // Remove from any restricted profiles that are sharing this account.
-                List<UserInfo> users = getUserManager().getUsers(true);
+                List<UserInfo> users = getUserManager().getAliveUsers();
                 for (UserInfo user : users) {
                     if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
                         removeSharedAccountAsUser(account, user.id, callingUid);
@@ -4267,7 +4267,7 @@
      */
     @NonNull
     public AccountAndUser[] getAllAccounts() {
-        final List<UserInfo> users = getUserManager().getUsers(true);
+        final List<UserInfo> users = getUserManager().getAliveUsers();
         final int[] userIds = new int[users.size()];
         for (int i = 0; i < userIds.length; i++) {
             userIds[i] = users.get(i).id;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c31d732..343e05d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2268,21 +2268,23 @@
                     clist.remove(0);
                 }
 
-                if (r.binding.service.app != null) {
-                    if (r.binding.service.app.whitelistManager) {
-                        updateWhitelistManagerLocked(r.binding.service.app);
+                final ProcessRecord app = r.binding.service.app;
+                if (app != null) {
+                    if (app.whitelistManager) {
+                        updateWhitelistManagerLocked(app);
                     }
                     // This could have made the service less important.
                     if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
-                        r.binding.service.app.treatLikeActivity = true;
-                        mAm.updateLruProcessLocked(r.binding.service.app,
-                                r.binding.service.app.hasClientActivities()
-                                || r.binding.service.app.treatLikeActivity, null);
+                        app.treatLikeActivity = true;
+                        mAm.updateLruProcessLocked(app,
+                                app.hasClientActivities()
+                                || app.treatLikeActivity, null);
                     }
+                    mAm.enqueueOomAdjTargetLocked(app);
                 }
             }
 
-            mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+            mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
 
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2d803437..2746c2c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2103,17 +2103,13 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             try {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(false);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
 
                 if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                         "meminfo", pw)) return;
                 PriorityDump.dump(mPriorityDumper, fd, pw, args);
             } finally {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(true);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
             }
         }
     }
@@ -2127,17 +2123,13 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             try {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(false);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
 
                 if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                         "gfxinfo", pw)) return;
                 mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
             } finally {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(true);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
             }
         }
     }
@@ -2151,17 +2143,13 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             try {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(false);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
 
                 if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                         "dbinfo", pw)) return;
                 mActivityManagerService.dumpDbInfo(fd, pw, args);
             } finally {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(true);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
             }
         }
     }
@@ -2207,9 +2195,7 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             try {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(false);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
 
                 if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                         "cacheinfo", pw)) {
@@ -2218,9 +2204,7 @@
 
                 mActivityManagerService.dumpBinderCacheContents(fd, pw, args);
             } finally {
-                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
-                    Process.enableFreezer(true);
-                }
+                mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
             }
         }
     }
@@ -5686,8 +5670,8 @@
                 "setProcessLimit()");
         synchronized (this) {
             mConstants.setOverrideMaxCachedProcesses(max);
+            trimApplicationsLocked(true, OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
         }
-        trimApplications(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
     }
 
     @Override
@@ -5778,9 +5762,7 @@
     }
 
     private boolean isAppBad(final String processName, final int uid) {
-        synchronized (this) {
-            return mAppErrors.isBadProcessLocked(processName, uid);
-        }
+        return mAppErrors.isBadProcess(processName, uid);
     }
 
     // NOTE: this is an internal method used by the OnShellCommand implementation only and should
@@ -7318,53 +7300,6 @@
         return uidRecord != null && !uidRecord.setIdle;
     }
 
-    /**
-     * Change the sched policy cgroup of a thread.
-     *
-     * <p>It is intended to be used in jni call of {@link Process#setThreadPriority(int, int)}.
-     * When a process is changing the priority of its threads, the sched policy of that
-     * thread may need to be changed accordingly - if the priority is changed to below
-     * or equal to {@link android.os.Process#THREAD_PRIORITY_BACKGROUND} or raising from it.
-     * However, because of the limitation of sepolicy, the thread priority change will
-     * fail for some processes. To solve it, we add this binder call in Activity Manager,
-     * so that the jni call of {@link Process#setThreadPriority(int, int)} could use it.
-     *
-     * @param tid tid of the thread to be changed.
-     * @param group The sched policy group to be changed to.
-     *
-     * @throws IllegalArgumentException if group is invalid.
-     * @throws SecurityException if no permission.
-     *
-     * @return Returns {@code true} if the sched policy is changed successfully;
-     *         {@code false} otherwise.
-     */
-    @Override
-    public boolean setSchedPolicyCgroup(final int tid, final int group) {
-        final int pid = Binder.getCallingPid();
-        final int tgid = Process.getThreadGroupLeader(tid);
-        final int pgroup = Process.getProcessGroup(pid);
-
-        // tid is not in the thread group of caller
-        if (pid != tgid) {
-            return false;
-        }
-
-        // sched group is higher than its main process
-        if (group > pgroup) {
-            return false;
-        }
-
-        try {
-            Process.setThreadGroup(tid, group);
-            return true;
-        } catch (IllegalArgumentException e) {
-            Slog.w(TAG, "Failed to set thread group, argument invalid:\n" + e);
-        } catch (SecurityException e) {
-            Slog.w(TAG, "Failed to set thread group, no permission:\n" + e);
-        }
-        return false;
-    }
-
     @Override
     public void setPersistentVrThread(int tid) {
         mActivityTaskManager.setPersistentVrThread(tid);
@@ -13995,7 +13930,8 @@
                                 r.resultAbort, false);
                         if (doNext) {
                             doTrim = true;
-                            r.queue.processNextBroadcast(false);
+                            r.queue.processNextBroadcastLocked(/* frommsg */ false,
+                                    /* skipOomAdj */ true);
                         }
                     }
 
@@ -14008,13 +13944,13 @@
                         rl.receiver.asBinder().unlinkToDeath(rl, 0);
                     }
                 }
-            }
 
-            // If we actually concluded any broadcasts, we might now be able
-            // to trim the recipients' apps from our working set
-            if (doTrim) {
-                trimApplications(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
-                return;
+                // If we actually concluded any broadcasts, we might now be able
+                // to trim the recipients' apps from our working set
+                if (doTrim) {
+                    trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
+                    return;
+                }
             }
 
         } finally {
@@ -15145,7 +15081,7 @@
                     r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
                 }
                 // updateOomAdjLocked() will be done here
-                trimApplicationsLocked(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
+                trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
             }
 
         } finally {
@@ -16155,6 +16091,32 @@
         return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll, oomAdjReason);
     }
 
+    /**
+     * Enqueue the given process into a todo list, and the caller should
+     * call {@link #updateOomAdjPendingTargetsLocked} to kick off a pass of the oom adj update.
+     */
+    @GuardedBy("this")
+    void enqueueOomAdjTargetLocked(ProcessRecord app) {
+        mOomAdjuster.enqueueOomAdjTargetLocked(app);
+    }
+
+    /**
+     * Remove the given process into a todo list.
+     */
+    @GuardedBy("this")
+    void removeOomAdjTargetLocked(ProcessRecord app, boolean procDied) {
+        mOomAdjuster.removeOomAdjTargetLocked(app, procDied);
+    }
+
+    /**
+     * Kick off an oom adj update pass for the pending targets which are enqueued via
+     * {@link #enqueueOomAdjTargetLocked}.
+     */
+    @GuardedBy("this")
+    void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+        mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason);
+    }
+
     static final class ProcStatsRunnable implements Runnable {
         private final ActivityManagerService mService;
         private final ProcessStatsService mProcessStats;
@@ -16563,16 +16525,17 @@
         mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist);
     }
 
-    final void trimApplications(String oomAdjReason) {
+    private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) {
         synchronized (this) {
-            trimApplicationsLocked(oomAdjReason);
+            trimApplicationsLocked(forceFullOomAdj, oomAdjReason);
         }
     }
 
     @GuardedBy("this")
-    final void trimApplicationsLocked(String oomAdjReason) {
+    private void trimApplicationsLocked(boolean forceFullOomAdj, String oomAdjReason) {
         // First remove any unused application processes whose package
         // has been removed.
+        boolean didSomething = false;
         for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
             final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
             if (!app.hasActivitiesOrRecentTasks()
@@ -16594,6 +16557,7 @@
                         // Ignore exceptions.
                     }
                 }
+                didSomething = true;
                 cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
                 mProcessList.mRemovedProcesses.remove(i);
 
@@ -16606,7 +16570,12 @@
 
         // Now update the oom adj for all processes. Don't skip this, since other callers
         // might be depending on it.
-        updateOomAdjLocked(oomAdjReason);
+        if (didSomething || forceFullOomAdj) {
+            updateOomAdjLocked(oomAdjReason);
+        } else {
+            // Process any pending oomAdj targets, it'll be a no-op if nothing is pending.
+            updateOomAdjPendingTargetsLocked(oomAdjReason);
+        }
     }
 
     /** This method sends the specified signal to each of the persistent apps */
@@ -16800,14 +16769,14 @@
                     }
                 }
 
-                Process.enableFreezer(false);
+                mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
 
                 final RemoteCallback intermediateCallback = new RemoteCallback(
                         new RemoteCallback.OnResultListener() {
                         @Override
                         public void onResult(Bundle result) {
                             finishCallback.sendResult(result);
-                            Process.enableFreezer(true);
+                            mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
                         }
                     }, null);
 
@@ -17144,8 +17113,7 @@
         @Override
         public int checkContentProviderUriPermission(Uri uri, int userId,
                 int callingUid, int modeFlags) {
-            return mCpHelper.checkContentProviderUriPermission(uri,
-                    userId, callingUid, modeFlags);
+            return mCpHelper.checkContentProviderUriPermission(uri, userId, callingUid, modeFlags);
         }
 
         @Override
@@ -17412,7 +17380,7 @@
 
         @Override
         public void trimApplications() {
-            ActivityManagerService.this.trimApplications(OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
+            ActivityManagerService.this.trimApplications(true, OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
         }
 
         public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) {
@@ -18738,4 +18706,16 @@
             mAppErrors.resetStateLocked();
         }
     }
+
+    @Override
+    public boolean enableAppFreezer(boolean enable) {
+        int callerUid = Binder.getCallingUid();
+
+        // Only system can toggle the freezer state
+        if (callerUid == SYSTEM_UID) {
+            return mOomAdjuster.mCachedAppOptimizer.enableFreezer(enable);
+        } else {
+            throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state ");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 5268359..5d429d3 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -50,6 +50,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -96,7 +97,11 @@
      * a minimum amount of time; they are removed from it when they are
      * later restarted (hopefully due to some user action).  The value is the
      * time it was added to the list.
+     *
+     * Access is synchronized on the container object itself, and no other
+     * locks may be acquired while holding that one.
      */
+    @GuardedBy("mBadProcesses")
     private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
 
 
@@ -114,76 +119,81 @@
         mProcessCrashTimes.clear();
         mProcessCrashTimesPersistent.clear();
         mProcessCrashShowDialogTimes.clear();
-        mBadProcesses.clear();
+        synchronized (mBadProcesses) {
+            mBadProcesses.clear();
+        }
     }
 
     void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) {
-        if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) {
-            return;
-        }
-
-        final long token = proto.start(fieldId);
-        final long now = SystemClock.uptimeMillis();
-        proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
-
-        if (!mProcessCrashTimes.getMap().isEmpty()) {
-            final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
-            final int procCount = pmap.size();
-            for (int ip = 0; ip < procCount; ip++) {
-                final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES);
-                final String pname = pmap.keyAt(ip);
-                final SparseArray<Long> uids = pmap.valueAt(ip);
-                final int uidCount = uids.size();
-
-                proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
-                for (int i = 0; i < uidCount; i++) {
-                    final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
-                    if (dumpPackage != null && (r == null || !r.pkgList.containsKey(dumpPackage))) {
-                        continue;
-                    }
-                    final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES);
-                    proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid);
-                    proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS,
-                            uids.valueAt(i));
-                    proto.end(etoken);
-                }
-                proto.end(ctoken);
+        synchronized (mBadProcesses) {
+            if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) {
+                return;
             }
 
-        }
+            final long token = proto.start(fieldId);
+            final long now = SystemClock.uptimeMillis();
+            proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
 
-        if (!mBadProcesses.getMap().isEmpty()) {
-            final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
-            final int processCount = pmap.size();
-            for (int ip = 0; ip < processCount; ip++) {
-                final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
-                final String pname = pmap.keyAt(ip);
-                final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
-                final int uidCount = uids.size();
+            if (!mProcessCrashTimes.getMap().isEmpty()) {
+                final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
+                final int procCount = pmap.size();
+                for (int ip = 0; ip < procCount; ip++) {
+                    final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES);
+                    final String pname = pmap.keyAt(ip);
+                    final SparseArray<Long> uids = pmap.valueAt(ip);
+                    final int uidCount = uids.size();
 
-                proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
-                for (int i = 0; i < uidCount; i++) {
-                    final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
-                    if (dumpPackage != null && (r == null
-                            || !r.pkgList.containsKey(dumpPackage))) {
-                        continue;
+                    proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
+                    for (int i = 0; i < uidCount; i++) {
+                        final int puid = uids.keyAt(i);
+                        final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+                        if (dumpPackage != null
+                                && (r == null || !r.pkgList.containsKey(dumpPackage))) {
+                            continue;
+                        }
+                        final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES);
+                        proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid);
+                        proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS,
+                                uids.valueAt(i));
+                        proto.end(etoken);
                     }
-                    final BadProcessInfo info = uids.valueAt(i);
-                    final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
-                    proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
-                    proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
-                    proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
-                    proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
-                    proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
-                    proto.end(etoken);
+                    proto.end(ctoken);
                 }
-                proto.end(btoken);
-            }
-        }
 
-        proto.end(token);
+            }
+
+            if (!mBadProcesses.getMap().isEmpty()) {
+                final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
+                final int processCount = pmap.size();
+                for (int ip = 0; ip < processCount; ip++) {
+                    final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
+                    final String pname = pmap.keyAt(ip);
+                    final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
+                    final int uidCount = uids.size();
+
+                    proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
+                    for (int i = 0; i < uidCount; i++) {
+                        final int puid = uids.keyAt(i);
+                        final ProcessRecord r = mService.getProcessNames().get(pname, puid);
+                        if (dumpPackage != null && (r == null
+                                || !r.pkgList.containsKey(dumpPackage))) {
+                            continue;
+                        }
+                        final BadProcessInfo info = uids.valueAt(i);
+                        final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
+                        proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
+                        proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
+                        proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
+                        proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
+                        proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
+                        proto.end(etoken);
+                    }
+                    proto.end(btoken);
+                }
+            }
+
+            proto.end(token);
+        }
     }
 
     boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
@@ -272,12 +282,16 @@
         return needSep;
     }
 
-    boolean isBadProcessLocked(final String processName, final int uid) {
-        return mBadProcesses.get(processName, uid) != null;
+    boolean isBadProcess(final String processName, final int uid) {
+        synchronized (mBadProcesses) {
+            return mBadProcesses.get(processName, uid) != null;
+        }
     }
 
-    void clearBadProcessLocked(final String processName, final int uid) {
-        mBadProcesses.remove(processName, uid);
+    void clearBadProcess(final String processName, final int uid) {
+        synchronized (mBadProcesses) {
+            mBadProcesses.remove(processName, uid);
+        }
     }
 
     void resetProcessCrashTimeLocked(final String processName, final int uid) {
@@ -747,8 +761,10 @@
                 if (!app.isolated) {
                     // XXX We don't have a way to mark isolated processes
                     // as bad, since they don't have a peristent identity.
-                    mBadProcesses.put(app.processName, app.uid,
-                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
+                    synchronized (mBadProcesses) {
+                        mBadProcesses.put(app.processName, app.uid,
+                                new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
+                    }
                     mProcessCrashTimes.remove(app.processName, app.uid);
                 }
                 app.bad = true;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 960d26b..6948f90 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -281,7 +281,7 @@
     }
 
     private final void processCurBroadcastLocked(BroadcastRecord r,
-            ProcessRecord app, boolean skipOomAdj) throws RemoteException {
+            ProcessRecord app) throws RemoteException {
         if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                 "Process cur broadcast " + r + " for app " + app);
         if (app.thread == null) {
@@ -297,9 +297,11 @@
         app.curReceivers.add(r);
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
         mService.mProcessList.updateLruProcessLocked(app, false, null);
-        if (!skipOomAdj) {
-            mService.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE);
-        }
+        // Make sure the oom adj score is updated before delivering the broadcast.
+        // Force an update, even if there are other pending requests, overall it still saves time,
+        // because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
+        mService.enqueueOomAdjTargetLocked(app);
+        mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
 
         // Tell the application to launch this receiver.
         r.intent.setComponent(r.curComponent);
@@ -340,7 +342,7 @@
             }
             try {
                 mPendingBroadcast = null;
-                processCurBroadcastLocked(br, app, false);
+                processCurBroadcastLocked(br, app);
                 didSomething = true;
             } catch (Exception e) {
                 Slog.w(TAG, "Exception in new application when starting receiver "
@@ -501,6 +503,7 @@
         r.intent.setComponent(null);
         if (r.curApp != null && r.curApp.curReceivers.contains(r)) {
             r.curApp.curReceivers.remove(r);
+            mService.enqueueOomAdjTargetLocked(r.curApp);
         }
         if (r.curFilter != null) {
             r.curFilter.receiverList.curBroadcast = null;
@@ -562,7 +565,7 @@
                 Slog.i(TAG, "Resuming delayed broadcast");
                 br.curComponent = null;
                 br.state = BroadcastRecord.IDLE;
-                processNextBroadcast(false);
+                processNextBroadcastLocked(false, false);
             }
         }
     }
@@ -604,7 +607,7 @@
     }
 
     private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
-            BroadcastFilter filter, boolean ordered, int index, boolean skipOomAdj) {
+            BroadcastFilter filter, boolean ordered, int index) {
         boolean skip = false;
         if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
                 filter.packageName, filter.owningUid)) {
@@ -790,10 +793,9 @@
                 // are already core system stuff so don't matter for this.
                 r.curApp = filter.receiverList.app;
                 filter.receiverList.app.curReceivers.add(r);
-                if (!skipOomAdj) {
-                    mService.updateOomAdjLocked(r.curApp, true,
-                            OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
-                }
+                mService.enqueueOomAdjTargetLocked(r.curApp);
+                mService.updateOomAdjPendingTargetsLocked(
+                        OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
             }
         }
         try {
@@ -827,6 +829,8 @@
                 filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
                 if (ordered) {
                     filter.receiverList.app.curReceivers.remove(r);
+                    // Something wrong, its oom adj could be downgraded, but not in a hurry.
+                    mService.enqueueOomAdjTargetLocked(r.curApp);
                 }
             }
             // And BroadcastRecord state related to ordered delivery, if appropriate
@@ -946,7 +950,7 @@
         return true;
     }
 
-    final void processNextBroadcast(boolean fromMsg) {
+    private void processNextBroadcast(boolean fromMsg) {
         synchronized (mService) {
             processNextBroadcastLocked(fromMsg, false);
         }
@@ -990,7 +994,7 @@
                         "Delivering non-ordered on [" + mQueueName + "] to registered "
                         + target + ": " + r);
                 deliverToRegisteredReceiverLocked(r,
-                        (BroadcastFilter) target, false, i, skipOomAdj);
+                        (BroadcastFilter) target, false, i);
             }
             addBroadcastToHistoryLocked(r);
             if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
@@ -1046,7 +1050,8 @@
                     // If we had finished the last ordered broadcast, then
                     // make sure all processes have correct oom and sched
                     // adjustments.
-                    mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
+                    mService.updateOomAdjPendingTargetsLocked(
+                            OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
                 }
 
                 // when we have no more ordered broadcast on this queue, stop logging
@@ -1288,7 +1293,7 @@
                     "Delivering ordered ["
                     + mQueueName + "] to registered "
                     + filter + ": " + r);
-            deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx, skipOomAdj);
+            deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
             if (r.receiver == null || !r.ordered) {
                 // The receiver has already finished, so schedule to
                 // process the next one.
@@ -1616,7 +1621,7 @@
                 app.addPackage(info.activityInfo.packageName,
                         info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
                 maybeAddAllowBackgroundActivityStartsToken(app, r);
-                processCurBroadcastLocked(r, app, skipOomAdj);
+                processCurBroadcastLocked(r, app);
                 return;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception when sending broadcast to "
@@ -1750,7 +1755,7 @@
                     ? r.curComponent.flattenToShortString() : "(null)"));
             r.curComponent = null;
             r.state = BroadcastRecord.IDLE;
-            processNextBroadcast(false);
+            processNextBroadcastLocked(false, false);
             return;
         }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index d9fde0f..c5047e5 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -208,6 +208,8 @@
     @GuardedBy("mPhenotypeFlagLock")
     private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
     private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER;
+    @GuardedBy("this")
+    private int mFreezerDisableCount = 1; // Freezer is initially disabled, until enabled
     private final Random mRandom = new Random();
     @GuardedBy("mPhenotypeFlagLock")
     @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE;
@@ -420,25 +422,82 @@
     }
 
     /**
-     * Determines whether the freezer is correctly supported by this system
+     * Enables or disabled the app freezer.
+     * @param enable Enables the freezer if true, disables it if false.
+     * @return true if the operation completed successfully, false otherwise.
+     */
+    public synchronized boolean enableFreezer(boolean enable) {
+        if (!mUseFreezer) {
+            return false;
+        }
+
+        if (enable) {
+            mFreezerDisableCount--;
+
+            if (mFreezerDisableCount > 0) {
+                return true;
+            } else if (mFreezerDisableCount < 0) {
+                Slog.e(TAG_AM, "unbalanced call to enableFreezer, ignoring");
+                mFreezerDisableCount = 0;
+                return false;
+            }
+        } else {
+            mFreezerDisableCount++;
+
+            if (mFreezerDisableCount > 1) {
+                return true;
+            }
+        }
+
+        try {
+            enableFreezerInternal(enable);
+            return true;
+        } catch (java.lang.RuntimeException e) {
+            if (enable) {
+                mFreezerDisableCount = 0;
+            } else {
+                mFreezerDisableCount = 1;
+            }
+
+            Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): "
+                    + e.toString());
+        }
+
+        return false;
+    }
+
+    /**
+     * Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
+     * but aren't removed from the freezer. While in this state, processes can be added or removed
+     * by using Process.setProcessFrozen(), but they wouldn't be actually frozen until the freezer
+     * is enabled. If enable == true all processes in the freezer are frozen.
+     *
+     * @param enable Specify whether to enable (true) or disable (false) the freezer.
+     *
+     * @hide
+     */
+    private static native void enableFreezerInternal(boolean enable);
+
+    /**
+     * Determines whether the freezer is supported by this system
      */
     public static boolean isFreezerSupported() {
         boolean supported = false;
         FileReader fr = null;
 
         try {
-            fr = new FileReader("/dev/freezer/frozen/freezer.killable");
-            int i = fr.read();
+            fr = new FileReader("/sys/fs/cgroup/freezer/cgroup.freeze");
+            char state = (char) fr.read();
 
-            if ((char) i == '1') {
+            if (state == '1' || state == '0') {
                 supported = true;
             } else {
-                Slog.w(TAG_AM, "Freezer killability is turned off, disabling freezer");
+                Slog.e(TAG_AM, "unexpected value in cgroup.freeze");
             }
         } catch (java.io.FileNotFoundException e) {
-            Slog.d(TAG_AM, "Freezer.killable not present, disabling freezer");
+            Slog.d(TAG_AM, "cgroup.freeze not present");
         } catch (Exception e) {
-            Slog.d(TAG_AM, "Unable to read freezer.killable, disabling freezer: " + e.toString());
+            Slog.d(TAG_AM, "unable to read cgroup.freeze: " + e.toString());
         }
 
         if (fr != null) {
@@ -471,6 +530,8 @@
 
         if (mUseFreezer && mFreezeHandler == null) {
             Slog.d(TAG_AM, "Freezer enabled");
+            enableFreezer(true);
+
             if (!mCachedAppOptimizerThread.isAlive()) {
                 mCachedAppOptimizerThread.start();
             }
@@ -479,6 +540,8 @@
 
             Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
                     Process.THREAD_GROUP_SYSTEM);
+        } else {
+            enableFreezer(false);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index bfba4af..1155569 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -198,22 +198,10 @@
 
             if (providerRunning) {
                 cpi = cpr.info;
-                String msg;
 
                 if (r != null && cpr.canRunHere(r)) {
-                    if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
-                        throw new SecurityException("Content provider lookup "
-                                + cpr.name.flattenToShortString()
-                                + " failed: association not allowed with package " + msg);
-                    }
-                    checkTime(startTime,
-                            "getContentProviderImpl: before checkContentProviderPermission");
-                    if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
-                            != null) {
-                        throw new SecurityException(msg);
-                    }
-                    checkTime(startTime,
-                            "getContentProviderImpl: after checkContentProviderPermission");
+                    checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
+                            cpr.name.flattenToShortString(), startTime);
 
                     // This provider has been published or is in the process
                     // of being published...  but it is also allowed to run
@@ -234,26 +222,14 @@
                 } catch (RemoteException e) {
                 }
 
-                if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
-                    throw new SecurityException(
-                            "Content provider lookup " + cpr.name.flattenToShortString()
-                                    + " failed: association not allowed with package " + msg);
-                }
-                checkTime(startTime,
-                        "getContentProviderImpl: before checkContentProviderPermission");
-                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
-                        != null) {
-                    throw new SecurityException(msg);
-                }
-                checkTime(startTime,
-                        "getContentProviderImpl: after checkContentProviderPermission");
+                checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
+                        cpr.name.flattenToShortString(), startTime);
 
                 final long origId = Binder.clearCallingIdentity();
 
                 checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
 
-                // In this case the provider instance already exists, so we can
-                // return it right away.
+                // In this case the provider instance already exists so we can return it right away.
                 conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
                         stable, true, startTime, mService.mProcessList);
 
@@ -328,19 +304,8 @@
                 cpi.applicationInfo = mService.getAppInfoForUser(cpi.applicationInfo, userId);
                 checkTime(startTime, "getContentProviderImpl: got app info for user");
 
-                String msg;
-                if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
-                    throw new SecurityException("Content provider lookup " + name
-                            + " failed: association not allowed with package " + msg);
-                }
-                checkTime(startTime,
-                        "getContentProviderImpl: before checkContentProviderPermission");
-                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
-                        != null) {
-                    throw new SecurityException(msg);
-                }
-                checkTime(startTime,
-                        "getContentProviderImpl: after checkContentProviderPermission");
+                checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, !singleton,
+                        name, startTime);
 
                 if (!mService.mProcessesReady && !cpi.processName.equals("system")) {
                     // If this content provider does not run in the system
@@ -352,10 +317,12 @@
 
                 // If system providers are not installed yet we aggressively crash to avoid
                 // creating multiple instance of these providers and then bad things happen!
-                if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()
-                        && "system".equals(cpi.processName)) {
-                    throw new IllegalStateException("Cannot access system provider: '"
-                            + cpi.authority + "' before system providers are installed!");
+                synchronized (this) {
+                    if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()
+                            && "system".equals(cpi.processName)) {
+                        throw new IllegalStateException("Cannot access system provider: '"
+                                + cpi.authority + "' before system providers are installed!");
+                    }
                 }
 
                 // Make sure that the user who owns this provider is running.  If not,
@@ -605,6 +572,23 @@
         return cpr.newHolder(conn, false);
     }
 
+    private void checkAssociationAndPermissionLocked(ProcessRecord callingApp, ProviderInfo cpi,
+            int callingUid, int userId, boolean checkUser, String cprName, long startTime) {
+        String msg;
+        if ((msg = checkContentProviderAssociation(callingApp, callingUid, cpi)) != null) {
+            throw new SecurityException("Content provider lookup " + cprName
+                    + " failed: association not allowed with package " + msg);
+        }
+        checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
+        if ((msg = checkContentProviderPermission(
+                    cpi, Binder.getCallingPid(), Binder.getCallingUid(), userId, checkUser,
+                    callingApp != null ? callingApp.toString() : null))
+                != null) {
+            throw new SecurityException(msg);
+        }
+        checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
+    }
+
     void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
         if (providers == null) {
             return;
@@ -623,7 +607,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-
+            boolean providersPublished = false;
             for (int i = 0, size = providers.size(); i < size; i++) {
                 ContentProviderHolder src = providers.get(i);
                 if (src == null || src.info == null || src.provider == null) {
@@ -636,6 +620,7 @@
                 if (DEBUG_MU) {
                     Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
                 }
+                providersPublished = true;
 
                 ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                 mProviderMap.putProviderByClass(comp, dst);
@@ -673,8 +658,19 @@
                     dst.onProviderPublishStatusLocked(true);
                 }
                 dst.mRestartCount = 0;
+            }
+
+            // update the app's oom adj value and each provider's usage stats
+            if (providersPublished) {
                 mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
-                maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority);
+                for (int i = 0, size = providers.size(); i < size; i++) {
+                    ContentProviderHolder src = providers.get(i);
+                    if (src == null || src.info == null || src.provider == null) {
+                        continue;
+                    }
+                    maybeUpdateProviderUsageStatsLocked(r,
+                            src.info.packageName, src.info.authority);
+                }
             }
 
             Binder.restoreCallingIdentity(origId);
@@ -702,7 +698,8 @@
                     throw new NullPointerException("connection is null");
                 }
                 if (decProviderCountLocked(conn, null, null, stable)) {
-                    mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
+                    mService.updateOomAdjLocked(conn.provider.proc,
+                            OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
                 }
             }
         } finally {
@@ -738,7 +735,8 @@
             ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
             if (localCpr.hasExternalProcessHandles()) {
                 if (localCpr.removeExternalProcessHandleLocked(token)) {
-                    mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
+                    mService.updateOomAdjLocked(localCpr.proc,
+                            OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
                 } else {
                     Slog.e(TAG, "Attempt to remove content provider " + localCpr
                             + " with no external reference for token: " + token + ".");
@@ -997,17 +995,19 @@
                     + "; expected to find a valid ContentProvider for this authority";
         }
 
+        final int callingPid = Binder.getCallingPid();
         ProcessRecord r;
+        final String appName;
         synchronized (mService.mPidsSelfLocked) {
-            r = mService.mPidsSelfLocked.get(Binder.getCallingPid());
-        }
-        if (r == null) {
-            return "Failed to find PID " + Binder.getCallingPid();
+            r = mService.mPidsSelfLocked.get(callingPid);
+            if (r == null) {
+                return "Failed to find PID " + callingPid;
+            }
+            appName = r.toString();
         }
 
-        synchronized (mService) {
-            return checkContentProviderPermissionLocked(cpi, r, userId, true);
-        }
+        return checkContentProviderPermission(cpi, callingPid, Binder.getCallingUid(),
+                userId, true, appName);
     }
 
     int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
@@ -1163,13 +1163,14 @@
                 }
             }
         }
-        if (providers != null) {
-            mService.mSystemThread.installSystemProviders(providers);
-        }
 
-        synchronized (mService) {
+        synchronized (this) {
+            if (providers != null) {
+                mService.mSystemThread.installSystemProviders(providers);
+            }
             mSystemProvidersInstalled = true;
         }
+
         mService.mConstants.start(mService.mContext.getContentResolver());
         mService.mCoreSettingsObserver = new CoreSettingsObserver(mService);
         mService.mActivityTaskManager.installSystemProviders();
@@ -1305,10 +1306,8 @@
      * given {@link ProviderInfo}. Final permission checking is always done
      * in {@link ContentProvider}.
      */
-    private String checkContentProviderPermissionLocked(ProviderInfo cpi, ProcessRecord r,
-            int userId, boolean checkUser) {
-        final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
-        final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
+    private String checkContentProviderPermission(ProviderInfo cpi, int callingPid, int callingUid,
+            int userId, boolean checkUser, String appName) {
         boolean checkedGrants = false;
         if (checkUser) {
             // Looking for cross-user grants before enforcing the typical cross-users permissions
@@ -1376,8 +1375,8 @@
             suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission;
         }
         final String msg = "Permission Denial: opening provider " + cpi.name
-                + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
-                + ", uid=" + callingUid + ")" + suffix;
+                + " from " + (appName != null ? appName : "(null)")
+                + " (pid=" + callingPid + ", uid=" + callingUid + ")" + suffix;
         Slog.w(TAG, msg);
         return msg;
     }
@@ -1398,18 +1397,17 @@
     }
 
     ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId, int pmFlags) {
-        ProviderInfo pi = null;
         ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
         if (cpr != null) {
-            pi = cpr.info;
+            return cpr.info;
         } else {
             try {
-                pi = AppGlobals.getPackageManager().resolveContentProvider(
+                return AppGlobals.getPackageManager().resolveContentProvider(
                         authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId);
             } catch (RemoteException ex) {
+                return null;
             }
         }
-        return pi;
     }
 
     private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
@@ -1419,7 +1417,6 @@
             return;
         }
 
-
         UserState userState = mService.mUserController.getStartedUserState(app.userId);
         if (userState == null) return;
         final long now = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index bad042c..fc33031 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -81,6 +81,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ServiceInfo;
 import android.os.Debug;
 import android.os.Handler;
@@ -94,6 +95,8 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.LongSparseArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -103,10 +106,13 @@
 import com.android.internal.compat.IPlatformCompat;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
+import com.android.server.compat.CompatChange;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowProcessController;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -209,12 +215,99 @@
     private final ProcessList mProcessList;
 
     private final int mNumSlots;
-    private ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
-    private ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
-    private ActiveUids mTmpUidRecords;
-    private ArrayDeque<ProcessRecord> mTmpQueue;
+    private final ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
+    private final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
+    private final ActiveUids mTmpUidRecords;
+    private final ArrayDeque<ProcessRecord> mTmpQueue;
+    private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
 
-    private final IPlatformCompat mPlatformCompat;
+    private final PlatformCompatCache mPlatformCompatCache;
+
+    private static class PlatformCompatCache {
+        private final PlatformCompat mPlatformCompat;
+        private final IPlatformCompat mIPlatformCompatProxy;
+        private final LongSparseArray<CacheItem> mCaches = new LongSparseArray<>();
+        private final boolean mCacheEnabled;
+
+        PlatformCompatCache(long[] compatChanges) {
+            IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+            if (b instanceof PlatformCompat) {
+                mPlatformCompat = (PlatformCompat) ServiceManager.getService(
+                        Context.PLATFORM_COMPAT_SERVICE);
+                for (long changeId: compatChanges) {
+                    mCaches.put(changeId, new CacheItem(mPlatformCompat, changeId));
+                }
+                mIPlatformCompatProxy = null;
+                mCacheEnabled = true;
+            } else {
+                // we are in UT where the platform_compat is not running within the same process
+                mIPlatformCompatProxy = IPlatformCompat.Stub.asInterface(b);
+                mPlatformCompat = null;
+                mCacheEnabled = false;
+            }
+        }
+
+        boolean isChangeEnabled(long changeId, ApplicationInfo app) throws RemoteException {
+            return mCacheEnabled ? mCaches.get(changeId).isChangeEnabled(app)
+                    : mIPlatformCompatProxy.isChangeEnabled(changeId, app);
+        }
+
+        void invalidate(ApplicationInfo app) {
+            for (int i = mCaches.size() - 1; i >= 0; i--) {
+                mCaches.valueAt(i).invalidate(app);
+            }
+        }
+
+        static class CacheItem implements CompatChange.ChangeListener {
+            private final PlatformCompat mPlatformCompat;
+            private final long mChangeId;
+            private final Object mLock = new Object();
+
+            private final ArrayMap<String, Pair<Boolean, WeakReference<ApplicationInfo>>> mCache =
+                    new ArrayMap<>();
+
+            CacheItem(PlatformCompat platformCompat, long changeId) {
+                mPlatformCompat = platformCompat;
+                mChangeId = changeId;
+                mPlatformCompat.registerListener(changeId, this);
+            }
+
+            boolean isChangeEnabled(ApplicationInfo app) {
+                synchronized (mLock) {
+                    final int index = mCache.indexOfKey(app.packageName);
+                    Pair<Boolean, WeakReference<ApplicationInfo>> p;
+                    if (index < 0) {
+                        p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app),
+                                new WeakReference<>(app));
+                        mCache.put(app.packageName, p);
+                        return p.first;
+                    }
+                    p = mCache.valueAt(index);
+                    if (p.second.get() == app) {
+                        return p.first;
+                    }
+                    // Cache is invalid, regenerate it
+                    p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app),
+                            new WeakReference<>(app));
+                    mCache.setValueAt(index, p);
+                    return p.first;
+                }
+            }
+
+            void invalidate(ApplicationInfo app) {
+                synchronized (mLock) {
+                    mCache.remove(app.packageName);
+                }
+            }
+
+            @Override
+            public void onCompatChange(String packageName) {
+                synchronized (mLock) {
+                    mCache.remove(packageName);
+                }
+            }
+        }
+    }
 
     OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
         this(service, processList, activeUids, createAdjusterThread());
@@ -263,8 +356,9 @@
         mTmpQueue = new ArrayDeque<ProcessRecord>(mConstants.CUR_MAX_CACHED_PROCESSES << 1);
         mNumSlots = ((ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) >> 1)
                 / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
-        mPlatformCompat = IPlatformCompat.Stub.asInterface(b);
+        mPlatformCompatCache = new PlatformCompatCache(new long[] {
+                PROCESS_CAPABILITY_CHANGE_ID, CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID
+        });
     }
 
     void initSettings() {
@@ -333,6 +427,8 @@
         // need to do a complete oom adj.
         final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
                 ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
+        // Check if this process is in the pending list too, remove from pending list if so.
+        mPendingProcessSet.remove(app);
         boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
                 SystemClock.uptimeMillis());
         if (oomAdjAll
@@ -360,6 +456,9 @@
             uidRec.reset();
         }
 
+        // Check if this process is in the pending list too, remove from pending list if so.
+        mPendingProcessSet.remove(app);
+
         computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false, true);
 
         boolean success = applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
@@ -401,6 +500,8 @@
     @GuardedBy("mService")
     void updateOomAdjLocked(String oomAdjReason) {
         final ProcessRecord topApp = mService.getTopAppLocked();
+        // Clear any pending ones because we are doing a full update now.
+        mPendingProcessSet.clear();
         updateOomAdjLockedInner(oomAdjReason, topApp , null, null, true, true);
     }
 
@@ -434,6 +535,8 @@
         app.containsCycle = false;
         app.procStateChanged = false;
         app.resetCachedInfo();
+        // Check if this process is in the pending list too, remove from pending list if so.
+        mPendingProcessSet.remove(app);
         boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false,
                 SystemClock.uptimeMillis());
         if (!success || (wasCached == app.isCached() && oldAdj != ProcessList.INVALID_ADJ
@@ -486,6 +589,10 @@
                 }
                 queue.offer(service);
                 service.mReachable = true;
+                // During scanning the reachable dependants, remove them from the pending oomadj
+                // targets list if it's possible, as they've been added into the immediate
+                // oomadj targets list 'processes' above.
+                mPendingProcessSet.remove(service);
             }
             for (int i = pr.conProviders.size() - 1; i >= 0; i--) {
                 ContentProviderConnection cpc = pr.conProviders.get(i);
@@ -499,6 +606,10 @@
                 }
                 queue.offer(provider);
                 provider.mReachable = true;
+                // During scanning the reachable dependants, remove them from the pending oomadj
+                // targets list if it's possible, as they've been added into the immediate
+                // oomadj targets list 'processes' above.
+                mPendingProcessSet.remove(provider);
             }
         }
 
@@ -523,12 +634,67 @@
             applyOomAdjLocked(app, false, SystemClock.uptimeMillis(),
                     SystemClock.elapsedRealtime());
         }
+        mTmpProcessList.clear();
         mService.mOomAdjProfiler.oomAdjEnded();
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         return true;
     }
 
     /**
+     * Enqueue the given process for a later oom adj update
+     */
+    @GuardedBy("mService")
+    void enqueueOomAdjTargetLocked(ProcessRecord app) {
+        if (app != null) {
+            mPendingProcessSet.add(app);
+        }
+    }
+
+    @GuardedBy("mService")
+    void removeOomAdjTargetLocked(ProcessRecord app, boolean procDied) {
+        if (app != null) {
+            mPendingProcessSet.remove(app);
+            if (procDied) {
+                mPlatformCompatCache.invalidate(app.info);
+            }
+        }
+    }
+
+    /**
+     * Kick off an oom adj update pass for the pending targets which are enqueued via
+     * {@link #enqueueOomAdjTargetLocked}.
+     */
+    @GuardedBy("mService")
+    void updateOomAdjPendingTargetsLocked(String oomAdjReason) {
+        if (mPendingProcessSet.isEmpty()) {
+            return;
+        }
+        final ProcessRecord topApp = mService.getTopAppLocked();
+
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+        mService.mOomAdjProfiler.oomAdjStarted();
+
+        final ArrayList<ProcessRecord> processes = mTmpProcessList;
+        final ActiveUids uids = mTmpUidRecords;
+        uids.clear();
+        processes.clear();
+        for (int i = mPendingProcessSet.size() - 1; i >= 0; i--) {
+            final ProcessRecord app = mPendingProcessSet.valueAt(i);
+            if (app.uidRecord != null) {
+                uids.put(app.uidRecord.uid, app.uidRecord);
+            }
+            processes.add(app);
+        }
+
+        updateOomAdjLockedInner(oomAdjReason, topApp, processes, uids, true, false);
+        processes.clear();
+        mPendingProcessSet.clear();
+
+        mService.mOomAdjProfiler.oomAdjEnded();
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /**
      * Update OomAdj for all processes within the given list (could be partial), or the whole LRU
      * list if the given list is null; when it's partial update, each process's client proc won't
      * get evaluated recursively here.
@@ -1555,7 +1721,7 @@
 
                     boolean enabled = false;
                     try {
-                        enabled = mPlatformCompat.isChangeEnabled(
+                        enabled = mPlatformCompatCache.isChangeEnabled(
                                 CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID, s.appInfo);
                     } catch (RemoteException e) {
                     }
@@ -1742,7 +1908,7 @@
                                 clientProcState = PROCESS_STATE_BOUND_TOP;
                                 boolean enabled = false;
                                 try {
-                                    enabled = mPlatformCompat.isChangeEnabled(
+                                    enabled = mPlatformCompatCache.isChangeEnabled(
                                             PROCESS_CAPABILITY_CHANGE_ID, client.info);
                                 } catch (RemoteException e) {
                                 }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 76089f8..87898d8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1528,15 +1528,18 @@
                 && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
                 && proc.lastCachedPss >= 4000) {
             // Turn this condition on to cause killing to happen regularly, for testing.
-            if (proc.baseProcessTracker != null) {
-                proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
-                for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                    ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
-                    FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
-                            proc.info.uid,
-                            holder.state.getName(),
-                            holder.state.getPackage(),
-                            proc.lastCachedPss, holder.appVersion);
+            synchronized (mService.mProcessStats.mLock) {
+                if (proc.baseProcessTracker != null) {
+                    proc.baseProcessTracker.reportCachedKill(
+                            proc.pkgList.mPkgList, proc.lastCachedPss);
+                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                        FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
+                                proc.info.uid,
+                                holder.state.getName(),
+                                holder.state.getPackage(),
+                                proc.lastCachedPss, holder.appVersion);
+                    }
                 }
             }
             proc.kill(Long.toString(proc.lastCachedPss) + "k from cached",
@@ -1549,16 +1552,18 @@
             if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
                     .lastCachedPss);
             if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) {
-                if (proc.baseProcessTracker != null) {
-                    proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList,
-                            proc.lastCachedPss);
-                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
-                        FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
-                                proc.info.uid,
-                                holder.state.getName(),
-                                holder.state.getPackage(),
-                                proc.lastCachedPss, holder.appVersion);
+                synchronized (mService.mProcessStats.mLock) {
+                    if (proc.baseProcessTracker != null) {
+                        proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList,
+                                proc.lastCachedPss);
+                        for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                            ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                            FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED,
+                                    proc.info.uid,
+                                    holder.state.getName(),
+                                    holder.state.getPackage(),
+                                    proc.lastCachedPss, holder.appVersion);
+                        }
                     }
                 }
                 proc.kill(Long.toString(proc.lastCachedPss) + "k from cached",
@@ -2360,7 +2365,7 @@
             if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
                 // If we are in the background, then check to see if this process
                 // is bad.  If so, we will just silently fail.
-                if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) {
+                if (mService.mAppErrors.isBadProcess(processName, info.uid)) {
                     if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
                             + "/" + processName);
                     return null;
@@ -2373,11 +2378,11 @@
                 if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
                         + "/" + processName);
                 mService.mAppErrors.resetProcessCrashTimeLocked(processName, info.uid);
-                if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) {
+                if (mService.mAppErrors.isBadProcess(processName, info.uid)) {
                     EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
                             UserHandle.getUserId(info.uid), info.uid,
                             info.processName);
-                    mService.mAppErrors.clearBadProcessLocked(processName, info.uid);
+                    mService.mAppErrors.clearBadProcess(processName, info.uid);
                     if (app != null) {
                         app.bad = false;
                     }
@@ -2636,6 +2641,7 @@
                 mLruProcessServiceStart--;
             }
             mLruProcesses.remove(lrui);
+            mService.removeOomAdjTargetLocked(app, true);
         }
     }
 
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index dfe8af1..ab36aec 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3926,7 +3926,7 @@
 
             if (!isAttributionTagValid) {
                 String msg = "attributionTag " + attributionTag + " not declared in"
-                        + "manifest of " + packageName;
+                        + " manifest of " + packageName;
                 try {
                     if (mPlatformCompat.isChangeEnabledByPackageName(
                             SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, packageName,
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 06ef58f..1615998 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -28,6 +28,7 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
+import android.media.ICapturePresetDevicesRoleDispatcher;
 import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.MediaMetrics;
 import android.os.Binder;
@@ -500,8 +501,8 @@
         sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
     }
 
-    /*package*/ void postSetModeOwnerPid(int pid) {
-        sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid);
+    /*package*/ void postSetModeOwnerPid(int pid, int mode) {
+        sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode);
     }
 
     /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
@@ -546,6 +547,25 @@
         mDeviceInventory.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
+    /*package*/ int setPreferredDevicesForCapturePresetSync(int capturePreset,
+            @NonNull List<AudioDeviceAttributes> devices) {
+        return mDeviceInventory.setPreferredDevicesForCapturePresetSync(capturePreset, devices);
+    }
+
+    /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+        return mDeviceInventory.clearPreferredDevicesForCapturePresetSync(capturePreset);
+    }
+
+    /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+    }
+
+    /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
+    }
+
     //---------------------------------------------------------------------
     // Communication with (to) AudioService
     //TODO check whether the AudioService methods are candidates to move here
@@ -694,6 +714,17 @@
         sendIMsgNoDelay(MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY, SENDMSG_QUEUE, strategy);
     }
 
+    /*package*/ void postSaveSetPreferredDevicesForCapturePreset(
+            int capturePreset, List<AudioDeviceAttributes> devices) {
+        sendILMsgNoDelay(
+                MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset, devices);
+    }
+
+    /*package*/ void postSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+        sendIMsgNoDelay(
+                MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
+    }
+
     //---------------------------------------------------------------------
     // Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
     // only call from a "handle"* method or "on"* method
@@ -977,7 +1008,9 @@
                         synchronized (mDeviceStateLock) {
                             if (mModeOwnerPid != msg.arg1) {
                                 mModeOwnerPid = msg.arg1;
-                                updateSpeakerphoneOn("setNewModeOwner");
+                                if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
+                                    updateSpeakerphoneOn("setNewModeOwner");
+                                }
                                 if (mModeOwnerPid != 0) {
                                     mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
                                 }
@@ -1096,6 +1129,17 @@
                 case MSG_CHECK_MUTE_MUSIC:
                     checkMessagesMuteMusic(0);
                     break;
+                case MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+                    final int capturePreset = msg.arg1;
+                    final List<AudioDeviceAttributes> devices =
+                            (List<AudioDeviceAttributes>) msg.obj;
+                    mDeviceInventory.onSaveSetPreferredDevicesForCapturePreset(
+                            capturePreset, devices);
+                } break;
+                case MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET: {
+                    final int capturePreset = msg.arg1;
+                    mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
+                } break;
                 default:
                     Log.wtf(TAG, "Invalid message " + msg.what);
             }
@@ -1172,6 +1216,9 @@
     private static final int MSG_CHECK_MUTE_MUSIC = 36;
     private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
 
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
+    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39;
+
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index fbf07cc..33a8a30 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -31,6 +31,7 @@
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
+import android.media.ICapturePresetDevicesRoleDispatcher;
 import android.media.IStrategyPreferredDevicesDispatcher;
 import android.media.MediaMetrics;
 import android.os.Binder;
@@ -140,6 +141,10 @@
     private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices =
             new ArrayMap<>();
 
+    // List of preferred devices of capture preset
+    private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset =
+            new ArrayMap<>();
+
     // the wrapper for AudioSystem static methods, allows us to spy AudioSystem
     private final @NonNull AudioSystemAdapter mAudioSystem;
 
@@ -154,6 +159,10 @@
     final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers =
             new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>();
 
+    // Monitoring of devices for role and capture preset
+    final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
+            new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
+
     /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
         mDeviceBroker = broker;
         mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
@@ -243,6 +252,9 @@
             pw.println("  " + prefix + " type:0x" + Integer.toHexString(keyType)
                     + " (" + AudioSystem.getDeviceName(keyType)
                     + ") addr:" + valueAddress); });
+        mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
+            pw.println("  " + prefix + "capturePreset:" + capturePreset
+                    + " devices:" + devices); });
     }
 
     //------------------------------------------------------------
@@ -270,6 +282,9 @@
                 mAudioSystem.setDevicesRoleForStrategy(
                         strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
         }
+        synchronized (mPreferredDevicesForCapturePreset) {
+            // TODO: call audiosystem to restore
+        }
     }
 
     // only public for mocking/spying
@@ -613,6 +628,20 @@
         dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>());
     }
 
+    /*package*/ void onSaveSetPreferredDevicesForCapturePreset(
+            int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+        mPreferredDevicesForCapturePreset.put(capturePreset, devices);
+        dispatchDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+    }
+
+    /*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) {
+        mPreferredDevicesForCapturePreset.remove(capturePreset);
+        dispatchDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED,
+                new ArrayList<AudioDeviceAttributes>());
+    }
+
     //------------------------------------------------------------
     //
 
@@ -651,6 +680,41 @@
         mPrefDevDispatchers.unregister(dispatcher);
     }
 
+    /*package*/ int setPreferredDevicesForCapturePresetSync(
+            int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+        final long identity = Binder.clearCallingIdentity();
+        final int status = mAudioSystem.setDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        Binder.restoreCallingIdentity(identity);
+
+        if (status == AudioSystem.SUCCESS) {
+            mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
+        }
+        return status;
+    }
+
+    /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+        final long identity = Binder.clearCallingIdentity();
+        final int status = mAudioSystem.clearDevicesRoleForCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
+        Binder.restoreCallingIdentity(identity);
+
+        if (status == AudioSystem.SUCCESS) {
+            mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
+        }
+        return status;
+    }
+
+    /*package*/ void registerCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDevRoleCapturePresetDispatchers.register(dispatcher);
+    }
+
+    /*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
+            @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
+        mDevRoleCapturePresetDispatchers.unregister(dispatcher);
+    }
+
     /**
      * Implements the communication with AudioSystem to (dis)connect a device in the native layers
      * @param connect true if connection
@@ -1306,6 +1370,19 @@
         mPrefDevDispatchers.finishBroadcast();
     }
 
+    private void dispatchDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+        final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
+        for (int i = 0; i < nbDispatchers; ++i) {
+            try {
+                mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
+                        capturePreset, role, devices);
+            } catch (RemoteException e) {
+            }
+        }
+        mDevRoleCapturePresetDispatchers.finishBroadcast();
+    }
+
     //----------------------------------------------------------
     // For tests only
 
diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java
index 9ebd75b..af0e978 100644
--- a/services/core/java/com/android/server/audio/AudioEventLogger.java
+++ b/services/core/java/com/android/server/audio/AudioEventLogger.java
@@ -60,7 +60,36 @@
          * @return the same instance of the event
          */
         public Event printLog(String tag) {
-            Log.i(tag, eventToString());
+            return printLog(ALOGI, tag);
+        }
+
+        public static final int ALOGI = 0;
+        public static final int ALOGE = 1;
+        public static final int ALOGW = 2;
+        public static final int ALOGV = 3;
+
+        /**
+         * Same as {@link #printLog(String)} with a log type
+         * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV}
+         * @param tag
+         * @return
+         */
+        public Event printLog(int type, String tag) {
+            switch (type) {
+                case ALOGI:
+                    Log.i(tag, eventToString());
+                    break;
+                case ALOGE:
+                    Log.e(tag, eventToString());
+                    break;
+                case ALOGW:
+                    Log.w(tag, eventToString());
+                    break;
+                case ALOGV:
+                default:
+                    Log.v(tag, eventToString());
+                    break;
+            }
             return this;
         }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
old mode 100755
new mode 100644
index 673ca1f..f63c2ee
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -26,6 +26,10 @@
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
 
+import static com.android.server.audio.AudioEventLogger.Event.ALOGE;
+import static com.android.server.audio.AudioEventLogger.Event.ALOGI;
+import static com.android.server.audio.AudioEventLogger.Event.ALOGW;
+
 import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -80,6 +84,7 @@
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioServerStateDispatcher;
 import android.media.IAudioService;
+import android.media.ICapturePresetDevicesRoleDispatcher;
 import android.media.IPlaybackConfigDispatcher;
 import android.media.IRecordingConfigDispatcher;
 import android.media.IRingtonePlayer;
@@ -287,6 +292,7 @@
     private static final int MSG_CHECK_MODE_FOR_UID = 31;
     private static final int MSG_STREAM_DEVICES_CHANGED = 32;
     private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
+    private static final int MSG_REINIT_VOLUMES = 34;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -669,6 +675,7 @@
 
     public AudioService(Context context, AudioSystemAdapter audioSystem,
             SystemServerAdapter systemServer) {
+        sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
         mContext = context;
         mContentResolver = context.getContentResolver();
         mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
@@ -888,6 +895,9 @@
                 mPrescaleAbsoluteVolume[i] = preScale[i];
             }
         }
+
+        // check on volume initialization
+        checkVolumeRangeInitialization("AudioService()");
     }
 
     public void systemReady() {
@@ -1015,11 +1025,15 @@
         if (!mSystemReady ||
                 (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) {
             Log.e(TAG, "Audioserver died.");
+            sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+                    "onAudioServerDied() audioserver died"));
             sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED, SENDMSG_NOOP, 0, 0,
                     null, 500);
             return;
         }
-        Log.e(TAG, "Audioserver started.");
+        Log.i(TAG, "Audioserver started.");
+        sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+                "onAudioServerDied() audioserver started"));
 
         updateAudioHalPids();
 
@@ -1054,14 +1068,7 @@
         mDeviceBroker.setForceUse_Async(AudioSystem.FOR_SYSTEM, forSys, "onAudioServerDied");
 
         // Restore stream volumes
-        int numStreamTypes = AudioSystem.getNumStreamTypes();
-        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-            VolumeStreamState streamState = mStreamStates[streamType];
-            AudioSystem.initStreamVolume(
-                streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10);
-
-            streamState.applyAllVolumes();
-        }
+        onReinitVolumes("after audioserver restart");
 
         // Restore audio volume groups
         restoreVolumeGroups();
@@ -1159,6 +1166,72 @@
         setMicMuteFromSwitchInput();
     }
 
+    private void onReinitVolumes(@NonNull String caller) {
+        final int numStreamTypes = AudioSystem.getNumStreamTypes();
+        // keep track of any error during stream volume initialization
+        int status = AudioSystem.AUDIO_STATUS_OK;
+        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+            VolumeStreamState streamState = mStreamStates[streamType];
+            final int res = AudioSystem.initStreamVolume(
+                    streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10);
+            if (res != AudioSystem.AUDIO_STATUS_OK) {
+                status = res;
+                Log.e(TAG, "Failed to initStreamVolume (" + res + ") for stream " + streamType);
+                // stream volume initialization failed, no need to try the others, it will be
+                // attempted again when MSG_REINIT_VOLUMES is handled
+                break;
+            }
+            streamState.applyAllVolumes();
+        }
+
+        // did it work? check based on status
+        if (status != AudioSystem.AUDIO_STATUS_OK) {
+            sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+                    caller + ": initStreamVolume failed with " + status + " will retry")
+                    .printLog(ALOGE, TAG));
+            sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
+                    caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS);
+            return;
+        }
+
+        // did it work? check based on min/max values of some basic streams
+        if (!checkVolumeRangeInitialization(caller)) {
+            return;
+        }
+
+        // success
+        sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+                caller + ": initStreamVolume succeeded").printLog(ALOGI, TAG));
+    }
+
+    /**
+     * Check volume ranges were properly initialized
+     * @return true if volume ranges were successfully initialized
+     */
+    private boolean checkVolumeRangeInitialization(String caller) {
+        boolean success = true;
+        final int[] basicStreams = { AudioSystem.STREAM_ALARM, AudioSystem.STREAM_RING,
+                AudioSystem.STREAM_MUSIC, AudioSystem.STREAM_VOICE_CALL,
+                AudioSystem.STREAM_ACCESSIBILITY };
+        for (int streamType : basicStreams) {
+            final AudioAttributes aa = new AudioAttributes.Builder()
+                    .setInternalLegacyStreamType(streamType).build();
+            if (AudioSystem.getMaxVolumeIndexForAttributes(aa) < 0
+                    || AudioSystem.getMinVolumeIndexForAttributes(aa) < 0) {
+                success = false;
+                break;
+            }
+        }
+        if (!success) {
+            sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+                    caller + ": initStreamVolume succeeded but invalid mix/max levels, will retry")
+                    .printLog(ALOGW, TAG));
+            sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
+                    caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS);
+        }
+        return success;
+    }
+
     private void onDispatchAudioServerStateChange(boolean state) {
         synchronized (mAudioServerStateListeners) {
             for (AsdProxy asdp : mAudioServerStateListeners.values()) {
@@ -1915,6 +1988,94 @@
         mDeviceBroker.unregisterStrategyPreferredDevicesDispatcher(dispatcher);
     }
 
+    /**
+     * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+     */
+    public int setPreferredDevicesForCapturePreset(
+            int capturePreset, List<AudioDeviceAttributes> devices) {
+        if (devices == null) {
+            return AudioSystem.ERROR;
+        }
+        enforceModifyAudioRoutingPermission();
+        final String logString = String.format(
+                "setPreferredDevicesForCapturePreset u/pid:%d/%d source:%d dev:%s",
+                Binder.getCallingUid(), Binder.getCallingPid(), capturePreset,
+                devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
+        sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+        if (devices.stream().anyMatch(device ->
+                device.getRole() == AudioDeviceAttributes.ROLE_OUTPUT)) {
+            Log.e(TAG, "Unsupported output routing in " + logString);
+            return AudioSystem.ERROR;
+        }
+
+        final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
+                capturePreset, devices);
+        if (status != AudioSystem.SUCCESS) {
+            Log.e(TAG, String.format("Error %d in %s)", status, logString));
+        }
+
+        return status;
+    }
+
+    /** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */
+    public int clearPreferredDevicesForCapturePreset(int capturePreset) {
+        enforceModifyAudioRoutingPermission();
+        final String logString = String.format(
+                "removePreferredDeviceForCapturePreset source:%d", capturePreset);
+        sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
+
+        final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
+        if (status != AudioSystem.SUCCESS) {
+            Log.e(TAG, String.format("Error %d in %s", status, logString));
+        }
+        return status;
+    }
+
+    /**
+     * @see AudioManager#getPreferredDevicesForCapturePreset(int)
+     */
+    public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+        enforceModifyAudioRoutingPermission();
+        List<AudioDeviceAttributes> devices = new ArrayList<>();
+        final long identity = Binder.clearCallingIdentity();
+        final int status = AudioSystem.getDevicesForRoleAndCapturePreset(
+                capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+        Binder.restoreCallingIdentity(identity);
+        if (status != AudioSystem.SUCCESS) {
+            Log.e(TAG, String.format("Error %d in getPreferredDeviceForCapturePreset(%d)",
+                    status, capturePreset));
+            return new ArrayList<AudioDeviceAttributes>();
+        } else {
+            return devices;
+        }
+    }
+
+    /**
+     * @see AudioManager#addOnPreferredDevicesForCapturePresetChangedListener(
+     *              Executor, OnPreferredDevicesForCapturePresetChangedListener)
+     */
+    public void registerCapturePresetDevicesRoleDispatcher(
+            @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        enforceModifyAudioRoutingPermission();
+        mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+    }
+
+    /**
+     * @see AudioManager#removeOnPreferredDevicesForCapturePresetChangedListener(
+     *              AudioManager.OnPreferredDevicesForCapturePresetChangedListener)
+     */
+    public void unregisterCapturePresetDevicesRoleDispatcher(
+            @Nullable ICapturePresetDevicesRoleDispatcher dispatcher) {
+        if (dispatcher == null) {
+            return;
+        }
+        enforceModifyAudioRoutingPermission();
+        mDeviceBroker.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
+    }
+
     /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
     public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
@@ -3747,13 +3908,15 @@
         private final IBinder mCb; // To be notified of client's death
         private final int mPid;
         private final int mUid;
-        private String mPackage;
+        private final boolean mIsPrivileged;
+        private final String mPackage;
         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
 
-        SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) {
+        SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
             mCb = cb;
             mPid = pid;
             mUid = uid;
+            mIsPrivileged = isPrivileged;
             mPackage = caller;
         }
 
@@ -3765,12 +3928,13 @@
                 if (index < 0) {
                     Log.w(TAG, "unregistered setMode() client died");
                 } else {
-                    newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG);
+                    newModeOwnerPid = setModeInt(
+                            AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG);
                 }
             }
             // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
             // SCO connections not started by the application changing the mode when pid changes
-            mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid);
+            mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode());
         }
 
         public int getPid() {
@@ -3796,6 +3960,10 @@
         public String getPackage() {
             return mPackage;
         }
+
+        public boolean isPrivileged() {
+            return mIsPrivileged;
+        }
     }
 
     /** @see AudioManager#setMode(int) */
@@ -3847,18 +4015,19 @@
                         + " without permission or being mode owner");
                 return;
             }
-            newModeOwnerPid = setModeInt(
-                mode, cb, callingPid, Binder.getCallingUid(), callingPackage);
+            newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(),
+                    hasModifyPhoneStatePermission, callingPackage);
         }
         // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
         // SCO connections not started by the application changing the mode when pid changes
-        mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid);
+        mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode());
     }
 
     // setModeInt() returns a valid PID if the audio mode was successfully set to
     // any mode other than NORMAL.
     @GuardedBy("mDeviceBroker.mSetModeLock")
-    private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) {
+    private int setModeInt(
+            int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
         if (DEBUG_MODE) {
             Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
                     + ", uid=" + uid + ", caller=" + caller + ")");
@@ -3910,7 +4079,7 @@
                 }
             } else {
                 if (hdlr == null) {
-                    hdlr = new SetModeDeathHandler(cb, pid, uid, caller);
+                    hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller);
                 }
                 // Register for client death notification
                 try {
@@ -3969,7 +4138,8 @@
             // change of mode may require volume to be re-applied on some devices
             updateAbsVolumeMultiModeDevices(oldMode, actualMode);
 
-            if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) {
+            if (actualMode == AudioSystem.MODE_IN_COMMUNICATION
+                    && !hdlr.isPrivileged()) {
                 sendMsg(mAudioHandler,
                         MSG_CHECK_MODE_FOR_UID,
                         SENDMSG_QUEUE,
@@ -4016,6 +4186,62 @@
         }
     }
 
+    /** @see AudioManager#adjustSuggestedStreamVolumeForUid(int, int, int, String, int, int, int) */
+    @Override
+    public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
+            @NonNull String packageName, int uid, int pid, UserHandle userHandle,
+            int targetSdkVersion) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Should only be called from system process");
+        }
+
+        final boolean hasModifyAudioSettings =
+                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+                        == PackageManager.PERMISSION_GRANTED;
+        // direction and stream type swap here because the public
+        // adjustSuggested has a different order than the other methods.
+        adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
+                hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+    }
+
+    /** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
+    @Override
+    public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+            @NonNull String packageName, int uid, int pid, UserHandle userHandle,
+            int targetSdkVersion) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Should only be called from system process");
+        }
+
+        if (direction != AudioManager.ADJUST_SAME) {
+            sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
+                    direction/*val1*/, flags/*val2*/,
+                    new StringBuilder(packageName).append(" uid:").append(uid)
+                    .toString()));
+        }
+        final boolean hasModifyAudioSettings =
+                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+                        == PackageManager.PERMISSION_GRANTED;
+        adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
+                hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+    }
+
+    /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
+    @Override
+    public void setStreamVolumeForUid(int streamType, int index, int flags,
+            @NonNull String packageName, int uid, int pid, UserHandle userHandle,
+            int targetSdkVersion) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Should only be called from system process");
+        }
+
+        final boolean hasModifyAudioSettings =
+                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+                        == PackageManager.PERMISSION_GRANTED;
+        setStreamVolume(streamType, index, flags, packageName, packageName, uid,
+                hasModifyAudioSettings);
+    }
+
     //==========================================================================================
     // Sound Effects
     //==========================================================================================
@@ -5663,7 +5889,15 @@
             mIndexMin = MIN_STREAM_VOLUME[streamType] * 10;
             mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex()
             mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
-            AudioSystem.initStreamVolume(streamType, mIndexMin / 10, mIndexMax / 10);
+            final int status = AudioSystem.initStreamVolume(
+                    streamType, mIndexMin / 10, mIndexMax / 10);
+            if (status != AudioSystem.AUDIO_STATUS_OK) {
+                sLifecycleLogger.log(new AudioEventLogger.StringEvent(
+                         "VSS() stream:" + streamType + " initStreamVolume=" + status)
+                        .printLog(ALOGE, TAG));
+                sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0,
+                        "VSS()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS);
+            }
 
             readSettings();
             mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
@@ -6519,8 +6753,8 @@
                                     CHECK_MODE_FOR_UID_PERIOD_MS);
                             break;
                         }
-                        // For now just log the fact that an app is hogging the audio mode.
-                        // TODO(b/160260850): remove abusive app from audio mode stack.
+                        setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(),
+                                h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID");
                         mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
                     }
                     break;
@@ -6534,6 +6768,10 @@
                 case MSG_UPDATE_VOLUME_STATES_FOR_DEVICE:
                     onUpdateVolumeStatesForAudioDevice(msg.arg1, (String) msg.obj);
                     break;
+
+                case MSG_REINIT_VOLUMES:
+                    onReinitVolumes((String) msg.obj);
+                    break;
             }
         }
     }
@@ -7463,12 +7701,16 @@
     //==========================================================================================
     // AudioService logging and dumpsys
     //==========================================================================================
+    static final int LOG_NB_EVENTS_LIFECYCLE = 20;
     static final int LOG_NB_EVENTS_PHONE_STATE = 20;
     static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30;
     static final int LOG_NB_EVENTS_FORCE_USE = 20;
     static final int LOG_NB_EVENTS_VOLUME = 40;
     static final int LOG_NB_EVENTS_DYN_POLICY = 10;
 
+    static final AudioEventLogger sLifecycleLogger = new AudioEventLogger(LOG_NB_EVENTS_LIFECYCLE,
+            "audio services lifecycle");
+
     final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
             "phone state (logged after successful call to AudioSystem.setPhoneState(int, int))");
 
@@ -7545,6 +7787,7 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
+        sLifecycleLogger.dump(pw);
         if (mAudioHandler != null) {
             pw.println("\nMessage handler (watch for unhandled messages):");
             mAudioHandler.dump(new PrintWriterPrinter(pw), "  ");
@@ -7884,43 +8127,6 @@
         }
 
         @Override
-        public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags,
-                String callingPackage, int uid, int pid) {
-            final boolean hasModifyAudioSettings =
-                    mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED;
-            // direction and stream type swap here because the public
-            // adjustSuggested has a different order than the other methods.
-            adjustSuggestedStreamVolume(direction, streamType, flags, callingPackage,
-                    callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
-        }
-
-        @Override
-        public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
-                String callingPackage, int uid, int pid) {
-            if (direction != AudioManager.ADJUST_SAME) {
-                sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_VOL_UID, streamType,
-                        direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
-                        .append(" uid:").append(uid).toString()));
-            }
-            final boolean hasModifyAudioSettings =
-                    mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                            == PackageManager.PERMISSION_GRANTED;
-            adjustStreamVolume(streamType, direction, flags, callingPackage,
-                    callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL);
-        }
-
-        @Override
-        public void setStreamVolumeForUid(int streamType, int direction, int flags,
-                String callingPackage, int uid, int pid) {
-            final boolean hasModifyAudioSettings =
-                    mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                            == PackageManager.PERMISSION_GRANTED;
-            setStreamVolume(streamType, direction, flags, callingPackage, callingPackage, uid,
-                    hasModifyAudioSettings);
-        }
-
-        @Override
         public int getRingerModeInternal() {
             return AudioService.this.getRingerModeInternal();
         }
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index a0e1ca7..ae64990 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -101,6 +101,40 @@
     }
 
     /**
+     * Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List))
+     * @param capturePreset
+     * @param role
+     * @param devices
+     * @return
+     */
+    public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+                                              @NonNull List<AudioDeviceAttributes> devices) {
+        return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices);
+    }
+
+    /**
+     * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int)}
+     * @param capturePreset
+     * @param role
+     * @param devicesToRemove
+     * @return
+     */
+    public int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+        return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove);
+    }
+
+    /**
+     * Same as {@link AudioSystem#}
+     * @param capturePreset
+     * @param role
+     * @return
+     */
+    public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
+        return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role);
+    }
+
+    /**
      * Same as {@link AudioSystem#setParameters(String)}
      * @param keyValuePairs
      * @return
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
old mode 100755
new mode 100644
index bbc29b0..f02bb8f
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.media.AudioFocusInfo;
@@ -94,8 +95,9 @@
         mContext = cntxt;
         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
         mFocusEnforcer = pfe;
-        mMultiAudioFocusEnabled = Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0) != 0;
+        final ContentResolver cr = mContext.getContentResolver();
+        mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr,
+                Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0;
     }
 
     protected void dump(PrintWriter pw) {
@@ -1081,8 +1083,9 @@
     public void updateMultiAudioFocus(boolean enabled) {
         Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )");
         mMultiAudioFocusEnabled = enabled;
-        Settings.System.putInt(mContext.getContentResolver(),
-                Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0);
+        final ContentResolver cr = mContext.getContentResolver();
+        Settings.System.putIntForUser(cr,
+                Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0, cr.getUserId());
         if (!mFocusStack.isEmpty()) {
             final FocusRequester fr = mFocusStack.peek();
             fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 73fc17a..9898d76 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -98,7 +98,11 @@
         }
 
         if (finish) {
-            mCallback.onClientFinished(this, false /* success */);
+            if (mCallback == null) {
+                Slog.e(TAG, "Callback is null, perhaps the client hasn't been started yet?");
+            } else {
+                mCallback.onClientFinished(this, false /* success */);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 9128359..4be596d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -195,6 +195,8 @@
                     if (listener != null) {
                         listener.onAuthenticationFailed(getSensorId());
                     }
+                } else {
+                    mAlreadyDone = true;
                 }
             }
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index dec40e3..3c9dddd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -176,6 +176,14 @@
 
     // TODO(b/157790417): Move this to the scheduler
     void binderDiedInternal(boolean clearListener) {
+        Slog.e(TAG, "Binder died, owner: " + getOwnerString()
+                + ", operation: " + this.getClass().getName());
+
+        if (isAlreadyDone()) {
+            Slog.w(TAG, "Binder died but client is finished, ignoring");
+            return;
+        }
+
         // If the current client dies we should cancel the current operation.
         if (this instanceof Interruptable) {
             Slog.e(TAG, "Binder died, cancelling client");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index 32bb2db..d2c35fe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -25,13 +25,14 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
 import android.hardware.face.Face;
-import android.hardware.face.FaceManager;
 import android.hardware.face.FaceSensorProperties;
 import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -110,6 +111,9 @@
         @Override
         public void onUserSwitching(int newUserId) {
             scheduleInternalCleanup(newUserId);
+            scheduleGetFeature(new Binder(), newUserId,
+                    BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION,
+                    null, mContext.getOpPackageName());
         }
     };
 
@@ -361,6 +365,9 @@
         if (halId != 0) {
             scheduleLoadAuthenticatorIds();
             scheduleInternalCleanup(ActivityManager.getCurrentUser());
+            scheduleGetFeature(new Binder(), ActivityManager.getCurrentUser(),
+                    BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION,
+                    null, mContext.getOpPackageName());
         } else {
             Slog.e(TAG, "Unable to set callback");
             mDaemon = null;
@@ -383,7 +390,7 @@
         //    is safe because authenticatorIds only change when A) new template has been enrolled,
         //    or B) all templates are removed.
         mHandler.post(() -> {
-            for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+            for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
                 final int targetUserId = user.id;
                 if (!mAuthenticatorIds.containsKey(targetUserId)) {
                     scheduleUpdateActiveUserWithoutHandler(targetUserId);
@@ -451,7 +458,7 @@
     }
 
     void scheduleGetFeature(@NonNull IBinder token, int userId, int feature,
-            @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+            @Nullable ClientMonitorCallbackConverter listener, @NonNull String opPackageName) {
         mHandler.post(() -> {
             final List<Face> faces = getEnrolledFaces(userId);
             if (faces.isEmpty()) {
@@ -462,10 +469,22 @@
             scheduleUpdateActiveUserWithoutHandler(userId);
 
             final int faceId = faces.get(0).getBiometricId();
-            final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
-                    mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
-                    opPackageName, mSensorId, feature, faceId);
-            mScheduler.scheduleClientMonitor(client);
+            final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
+                    token, listener, userId, opPackageName, mSensorId, feature, faceId);
+            mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
+                @Override
+                public void onClientFinished(
+                        @NonNull ClientMonitor<?> clientMonitor, boolean success) {
+                    if (success && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) {
+                        final int settingsValue = client.getValue() ? 1 : 0;
+                        Slog.d(TAG, "Updating attention value for user: " + userId
+                                + " to value: " + settingsValue);
+                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
+                                settingsValue, userId);
+                    }
+                }
+            });
         });
     }
 
@@ -480,7 +499,8 @@
      *    notifying the previous caller that the interrupting operation is complete (e.g. the
      *    interrupting client's challenge has been revoked, so that the interrupted client can
      *    start retry logic if necessary). See
-     *    {@link FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)}
+     *    {@link
+     *android.hardware.face.FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)}
      * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second
      * option seems better as it prioritizes the new operation, which is user-facing.
      */
@@ -490,8 +510,15 @@
             if (mCurrentChallengeOwner != null) {
                 Slog.w(TAG, "Current challenge owner: " + mCurrentChallengeOwner
                         + ", interrupted by: " + opPackageName);
+                final ClientMonitorCallbackConverter listener =
+                        mCurrentChallengeOwner.getListener();
+                if (listener == null) {
+                    Slog.w(TAG, "Null listener, skip sending interruption callback");
+                    return;
+                }
+
                 try {
-                    mCurrentChallengeOwner.getListener().onChallengeInterrupted(mSensorId);
+                    listener.onChallengeInterrupted(mSensorId);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Unable to notify challenge interrupted", e);
                 }
@@ -504,7 +531,7 @@
                 @Override
                 public void onClientStarted(@NonNull ClientMonitor<?> clientMonitor) {
                     if (client != clientMonitor) {
-                        Slog.e(TAG, "scheduleGenerateChallenge, mismatched client."
+                        Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
                                 + " Expecting: " + client + ", received: " + clientMonitor);
                         return;
                     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
index 8c7b99d..33b2b6a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
@@ -17,6 +17,7 @@
 package com.android.server.biometrics.sensors.face;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
@@ -39,9 +40,10 @@
 
     private final int mFeature;
     private final int mFaceId;
+    private boolean mValue;
 
     FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int sensorId, int feature, int faceId) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
                 BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
@@ -54,7 +56,9 @@
     @Override
     public void unableToStart() {
         try {
-            getListener().onFeatureGet(false /* success */, mFeature, false /* value */);
+            if (getListener() != null) {
+                getListener().onFeatureGet(false /* success */, mFeature, false /* value */);
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to send error", e);
         }
@@ -70,11 +74,18 @@
     protected void startHalOperation() {
         try {
             final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId);
-            getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value);
+            mValue = result.value;
+            if (getListener() != null) {
+                getListener().onFeatureGet(result.status == Status.OK, mFeature, mValue);
+            }
             mCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to getFeature", e);
             mCallback.onClientFinished(this, false /* success */);
         }
     }
+
+    boolean getValue() {
+        return mValue;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index b689106..c6664f4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -25,9 +25,9 @@
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.face.Face;
+import android.hardware.face.FaceSensorProperties;
 import android.hardware.face.IFaceService;
 import android.hardware.face.IFaceServiceReceiver;
-import android.hardware.face.FaceSensorProperties;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.NativeHandle;
@@ -303,7 +303,8 @@
         public void getFeature(final IBinder token, int userId, int feature,
                 IFaceServiceReceiver receiver, final String opPackageName) {
             Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
-            mFace10.scheduleGetFeature(token, userId, feature, receiver, opPackageName);
+            mFace10.scheduleGetFeature(token, userId, feature,
+                    new ClientMonitorCallbackConverter(receiver), opPackageName);
         }
 
         @Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
index c5c2822..3754bd7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -440,7 +440,7 @@
         //    is safe because authenticatorIds only change when A) new template has been enrolled,
         //    or B) all templates are removed.
         mHandler.post(() -> {
-            for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+            for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
                 final int targetUserId = user.id;
                 if (!mAuthenticatorIds.containsKey(targetUserId)) {
                     scheduleUpdateActiveUserWithoutHandler(targetUserId);
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index a0bc7d8..5d2f512 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -57,10 +57,12 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.autofill.AutofillManagerInternal;
+import android.widget.Toast;
 
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.SystemService.TargetUser;
+import com.android.server.UiThread;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -391,7 +393,9 @@
                     return null;
                 }
                 addActiveOwnerLocked(intendingUid, pkg);
-                return getClipboard(intendingUserId).primaryClip;
+                PerUserClipboard clipboard = getClipboard(intendingUserId);
+                maybeNotify(pkg, intendingUid, intendingUserId, clipboard);
+                return clipboard.primaryClip;
             }
         }
 
@@ -821,4 +825,65 @@
 
         return appOpsResult == AppOpsManager.MODE_ALLOWED;
     }
+
+    /**
+     * Potentially notifies the user (via a toast) about an app accessing the clipboard.
+     * TODO(b/167676460): STOPSHIP as we don't want this code as-is to launch. Just an experiment.
+     */
+    private void maybeNotify(String callingPackage, int uid, @UserIdInt int userId,
+            PerUserClipboard clipboard) {
+        if (clipboard.primaryClip == null) {
+            return;
+        }
+        if (Settings.Global.getInt(getContext().getContentResolver(),
+                "clipboard_access_toast_enabled", 0) == 0) {
+            return;
+        }
+        // Don't notify if the app accessing the clipboard is the same as the current owner.
+        if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
+            return;
+        }
+        // Exclude some special cases. It's a bit wasteful to check these again here, but for now
+        // beneficial to have all the logic contained in this single (probably temporary) method.
+        String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
+                Settings.Secure.DEFAULT_INPUT_METHOD, userId);
+        if (!TextUtils.isEmpty(defaultIme)) {
+            final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
+            if (imePkg.equals(callingPackage)) {
+                return;
+            }
+        }
+        if (mContentCaptureInternal != null
+                && mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId)) {
+            return;
+        }
+        if (mAutofillInternal != null
+                && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
+            return;
+        }
+        // Load the labels for the calling app and the app that set the clipboard content.
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final IPackageManager pm = AppGlobals.getPackageManager();
+            String message;
+            final CharSequence callingLabel = mPm.getApplicationLabel(
+                    pm.getApplicationInfo(callingPackage, 0, userId));
+            final String[] packagesForUid = pm.getPackagesForUid(clipboard.primaryClipUid);
+            if (packagesForUid != null && packagesForUid.length > 0) {
+                final CharSequence clipLabel = mPm.getApplicationLabel(
+                        pm.getApplicationInfo(packagesForUid[0], 0,
+                                UserHandle.getUserId(clipboard.primaryClipUid)));
+                message = callingLabel + " pasted from " + clipLabel;
+            } else {
+                message = callingLabel + " pasted from clipboard";
+            }
+            Slog.i(TAG, message);
+            Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT)
+                    .show();
+        } catch (RemoteException e) {
+            /* ignore */
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 7c8fb5a..1f0066a 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -29,6 +29,7 @@
 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
 import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
 import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.ERROR_STOP_REASON_UNINITIALIZED;
 import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
 import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
 import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
@@ -152,6 +153,7 @@
         private static final int STARTED = 3;
         private static final int STOPPING = 4;
         private int mStartedState = NOT_STARTED;
+        private int mStopReason = ERROR_STOP_REASON_UNINITIALIZED;
 
         KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback,
                 @NonNull NetworkAgentInfo nai,
@@ -365,6 +367,11 @@
                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
                 }
             }
+            // Store the reason of stopping, and report it after the keepalive is fully stopped.
+            if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
+                throw new IllegalStateException("Unexpected stop reason: " + mStopReason);
+            }
+            mStopReason = reason;
             Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
                     + ": " + reason);
             switch (mStartedState) {
@@ -403,24 +410,6 @@
                     Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e);
                 }
             }
-
-            if (reason == SUCCESS) {
-                try {
-                    mCallback.onStopped();
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Discarded onStop callback: " + reason);
-                }
-            } else if (reason == DATA_RECEIVED) {
-                try {
-                    mCallback.onDataReceived();
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Discarded onDataReceived callback: " + reason);
-                }
-            } else {
-                notifyErrorCallback(mCallback, reason);
-            }
-
-            unlinkDeathRecipient();
         }
 
         void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
@@ -505,12 +494,37 @@
             Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName);
             return;
         }
+
+        // Remove the keepalive from hash table so the slot can be considered available when reusing
+        // it.
         networkKeepalives.remove(slot);
         Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", "
                 + networkKeepalives.size() + " remains.");
         if (networkKeepalives.isEmpty()) {
             mKeepalives.remove(nai);
         }
+
+        // Notify app that the keepalive is stopped.
+        final int reason = ki.mStopReason;
+        if (reason == SUCCESS) {
+            try {
+                ki.mCallback.onStopped();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Discarded onStop callback: " + reason);
+            }
+        } else if (reason == DATA_RECEIVED) {
+            try {
+                ki.mCallback.onDataReceived();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Discarded onDataReceived callback: " + reason);
+            }
+        } else if (reason == ERROR_STOP_REASON_UNINITIALIZED) {
+            throw new IllegalStateException("Unexpected stop reason: " + reason);
+        } else {
+            notifyErrorCallback(ki.mCallback, reason);
+        }
+
+        ki.unlinkDeathRecipient();
     }
 
     public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index a75a80a..4c63eb4 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -174,7 +174,7 @@
             netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
         }
 
-        List<UserInfo> users = mUserManager.getUsers(true);  // exclude dying users
+        List<UserInfo> users = mUserManager.getAliveUsers();
         if (users != null) {
             for (UserInfo user : users) {
                 mUsers.add(user.id);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 5484bfc..7175489 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1463,7 +1463,7 @@
             final long token = Binder.clearCallingIdentity();
             List<UserInfo> users;
             try {
-                users = UserManager.get(mContext).getUsers(true);
+                users = UserManager.get(mContext).getAliveUsers();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index ec12a97..b33aa0a 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -367,7 +367,7 @@
     }
 
     private void removeStaleAccounts() {
-        for (UserInfo user : mUserManager.getUsers(true)) {
+        for (UserInfo user : mUserManager.getAliveUsers()) {
             // Skip any partially created/removed users
             if (user.partial) continue;
             Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
@@ -777,7 +777,7 @@
         if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) {
             return;
         }
-        List<UserInfo> users = mUserManager.getUsers(true);
+        List<UserInfo> users = mUserManager.getAliveUsers();
         final int userCount = users.size();
         for (int i = 0; i < userCount; i++) {
             UserHandle userHandle = users.get(i).getUserHandle();
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index ad3cd67..73f7889 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -493,6 +493,10 @@
                         == Display.COLOR_MODE_DISPLAY_P3;
                 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                         mDisplayManagerInternal.systemScreenshot(mDisplayId);
+                if (screenshotBuffer == null) {
+                    Slog.e(TAG, "Failed to take screenshot. Buffer is null");
+                    return false;
+                }
                 s.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
                         screenshotBuffer.getColorSpace());
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2c632d9..4a12ee7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -83,6 +83,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.EventLog;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
@@ -283,6 +284,7 @@
 
     // Temporary display info, used for comparing display configurations.
     private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
+    private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo();
 
     // Temporary viewports, used when sending new viewport information to the
     // input system.  May be used outside of the lock but only on the handler thread.
@@ -507,7 +509,8 @@
         mDisplayTransactionListeners.remove(listener);
     }
 
-    private void setDisplayInfoOverrideFromWindowManagerInternal(
+    @VisibleForTesting
+    void setDisplayInfoOverrideFromWindowManagerInternal(
             int displayId, DisplayInfo info) {
         synchronized (mSyncRoot) {
             LogicalDisplay display = mLogicalDisplays.get(displayId);
@@ -935,7 +938,8 @@
         adapter.registerLocked();
     }
 
-    private void handleDisplayDeviceAdded(DisplayDevice device) {
+    @VisibleForTesting
+    void handleDisplayDeviceAdded(DisplayDevice device) {
         synchronized (mSyncRoot) {
             handleDisplayDeviceAddedLocked(device);
         }
@@ -947,7 +951,6 @@
             Slog.w(TAG, "Attempted to add already added display device: " + info);
             return;
         }
-
         Slog.i(TAG, "Display device added: " + info);
         device.mDebugLastLoggedDeviceInfo = info;
 
@@ -960,7 +963,8 @@
         scheduleTraversalLocked(false);
     }
 
-    private void handleDisplayDeviceChanged(DisplayDevice device) {
+    @VisibleForTesting
+    void handleDisplayDeviceChanged(DisplayDevice device) {
         synchronized (mSyncRoot) {
             DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
             if (!mDisplayDevices.contains(device)) {
@@ -1234,6 +1238,7 @@
             LogicalDisplay display = mLogicalDisplays.valueAt(i);
 
             mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+            display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
             display.updateLocked(mDisplayDevices);
             if (!display.isValidLocked()) {
                 mLogicalDisplays.removeAt(i);
@@ -1242,6 +1247,15 @@
             } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
                 handleLogicalDisplayChanged(displayId, display);
                 changed = true;
+            } else {
+                // While applications shouldn't know nor care about the non-overridden info, we
+                // still need to let WindowManager know so it can update its own internal state for
+                // things like display cutouts.
+                display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
+                if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
+                    handleLogicalDisplayChanged(displayId, display);
+                    changed = true;
+                }
             }
         }
         return changed;
@@ -2176,6 +2190,8 @@
 
             if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
                 if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) {
+                    EventLog.writeEvent(0x534e4554, "162627132", callingUid,
+                            "Attempt to create a trusted display without holding permission!");
                     throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to "
                             + "create a trusted virtual display.");
                 }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 3c05080..cc6687f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -869,10 +869,11 @@
         }
 
         private void updateRefreshRateSettingLocked() {
-            float minRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
-                    Settings.System.MIN_REFRESH_RATE, 0f);
-            float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
-                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
+            final ContentResolver cr = mContext.getContentResolver();
+            float minRefreshRate = Settings.System.getFloatForUser(cr,
+                    Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
+            float peakRefreshRate = Settings.System.getFloatForUser(cr,
+                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
         }
 
@@ -1301,8 +1302,9 @@
         }
         // TODO: brightnessfloat: make it use float not int
         private void onBrightnessChangedLocked() {
-            int brightness = Settings.System.getInt(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS, -1);
+            final ContentResolver cr = mContext.getContentResolver();
+            int brightness = Settings.System.getIntForUser(cr,
+                    Settings.System.SCREEN_BRIGHTNESS, -1, cr.getUserId());
 
             Vote vote = null;
             boolean insideZone = isInsideZone(brightness, mAmbientLux);
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceAction.java b/services/core/java/com/android/server/hdmi/ActiveSourceAction.java
index c90f297..1796027 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceAction.java
@@ -51,7 +51,7 @@
                             Constants.MENU_STATE_ACTIVATED));
         }
 
-        source().setActiveSource(logicalAddress, physicalAddress);
+        source().setActiveSource(logicalAddress, physicalAddress, "ActiveSourceAction");
         mState = STATE_FINISHED;
         finish();
         return true;
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 01547c1..8405bbe 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -17,9 +17,9 @@
 package com.android.server.hdmi;
 
 import android.annotation.Nullable;
-import android.hardware.hdmi.IHdmiControlCallback;
-import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -69,7 +69,7 @@
 
         if (!tv.isProhibitMode()) {
             ActiveSource old = ActiveSource.of(tv.getActiveSource());
-            tv.updateActiveSource(newActive);
+            tv.updateActiveSource(newActive, "ActiveSourceHandler");
             boolean notifyInputChange = (mCallback == null);
             if (!old.equals(newActive)) {
                 tv.setPrevPortId(tv.getActivePortId());
@@ -85,7 +85,7 @@
                 HdmiCecMessage activeSourceCommand = HdmiCecMessageBuilder.buildActiveSource(
                         current.logicalAddress, current.physicalAddress);
                 mService.sendCecCommand(activeSourceCommand);
-                tv.updateActiveSource(current);
+                tv.updateActiveSource(current, "ActiveSourceHandler");
                 invokeCallback(HdmiControlManager.RESULT_SUCCESS);
             } else {
                 tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress, true,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 66652ca..7dc4d6e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -688,16 +688,24 @@
     }
 
     void dump(final IndentingPrintWriter pw) {
+        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
         for (int i = 0; i < mLocalDevices.size(); ++i) {
             pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":");
             pw.increaseIndent();
             mLocalDevices.valueAt(i).dump(pw);
+
+            pw.println("Active Source history:");
+            pw.increaseIndent();
+            for (Dumpable activeSourceEvent : mLocalDevices.valueAt(i).getActiveSourceHistory()) {
+                activeSourceEvent.dump(pw, sdf);
+            }
+            pw.decreaseIndent();
             pw.decreaseIndent();
         }
 
         pw.println("CEC message history:");
         pw.increaseIndent();
-        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         for (Dumpable record : mMessageHistory) {
             record.dump(pw, sdf);
         }
@@ -885,7 +893,7 @@
         @Override
         public void serviceDied(long cookie) {
             if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) {
-                HdmiLogger.error(TAG, "Service died cokkie : " + cookie + "; reconnecting");
+                HdmiLogger.error("Service died cookie : " + cookie + "; reconnecting");
                 connectToHal();
             }
         }
@@ -917,7 +925,7 @@
         }
     }
 
-    private abstract static class Dumpable {
+    public abstract static class Dumpable {
         protected final long mTime;
 
         Dumpable() {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 86e6a32..b88a37e 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -38,10 +38,13 @@
 import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
 
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
 
 /**
  * Class that models a logical CEC device hosted in this system. Handles initialization, CEC
@@ -50,6 +53,7 @@
 abstract class HdmiCecLocalDevice {
     private static final String TAG = "HdmiCecLocalDevice";
 
+    private static final int MAX_HDMI_ACTIVE_SOURCE_HISTORY = 10;
     private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1;
     private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2;
     // Timeout in millisecond for device clean up (5s).
@@ -68,6 +72,10 @@
     protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE;
     protected int mLastKeyRepeatCount = 0;
 
+    // Stores recent changes to the active source in the CEC network.
+    private final ArrayBlockingQueue<HdmiCecController.Dumpable> mActiveSourceHistory =
+            new ArrayBlockingQueue<>(MAX_HDMI_ACTIVE_SOURCE_HISTORY);
+
     static class ActiveSource {
         int logicalAddress;
         int physicalAddress;
@@ -893,16 +901,16 @@
         return mService.getLocalActiveSource();
     }
 
-    void setActiveSource(ActiveSource newActive) {
-        setActiveSource(newActive.logicalAddress, newActive.physicalAddress);
+    void setActiveSource(ActiveSource newActive, String caller) {
+        setActiveSource(newActive.logicalAddress, newActive.physicalAddress, caller);
     }
 
-    void setActiveSource(HdmiDeviceInfo info) {
-        setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
+    void setActiveSource(HdmiDeviceInfo info, String caller) {
+        setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress(), caller);
     }
 
-    void setActiveSource(int logicalAddress, int physicalAddress) {
-        mService.setActiveSource(logicalAddress, physicalAddress);
+    void setActiveSource(int logicalAddress, int physicalAddress, String caller) {
+        mService.setActiveSource(logicalAddress, physicalAddress, caller);
         mService.setLastInputForMhl(Constants.INVALID_PORT_ID);
     }
 
@@ -1120,6 +1128,20 @@
                 HdmiCecMessageBuilder.buildUserControlReleased(mAddress, targetAddress));
     }
 
+    void addActiveSourceHistoryItem(ActiveSource activeSource, boolean isActiveSource,
+            String caller) {
+        ActiveSourceHistoryRecord record = new ActiveSourceHistoryRecord(activeSource,
+                isActiveSource, caller);
+        if (!mActiveSourceHistory.offer(record)) {
+            mActiveSourceHistory.poll();
+            mActiveSourceHistory.offer(record);
+        }
+    }
+
+    public ArrayBlockingQueue<HdmiCecController.Dumpable> getActiveSourceHistory() {
+        return this.mActiveSourceHistory;
+    }
+
     /** Dump internal status of HdmiCecLocalDevice object. */
     protected void dump(final IndentingPrintWriter pw) {
         pw.println("mDeviceType: " + mDeviceType);
@@ -1152,4 +1174,29 @@
         }
         return finalMask | myPhysicalAddress;
     }
+
+    private static final class ActiveSourceHistoryRecord extends HdmiCecController.Dumpable {
+        private final ActiveSource mActiveSource;
+        private final boolean mIsActiveSource;
+        private final String mCaller;
+
+        private ActiveSourceHistoryRecord(ActiveSource mActiveSource, boolean mIsActiveSource,
+                String caller) {
+            this.mActiveSource = mActiveSource;
+            this.mIsActiveSource = mIsActiveSource;
+            this.mCaller = caller;
+        }
+
+        @Override
+        void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) {
+            pw.print("time=");
+            pw.print(sdf.format(new Date(mTime)));
+            pw.print(" active source=");
+            pw.print(mActiveSource);
+            pw.print(" isActiveSource=");
+            pw.print(mIsActiveSource);
+            pw.print(" from=");
+            pw.println(mCaller);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index af81597..68473c1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -361,7 +361,8 @@
         assertRunOnServiceThread();
         // Invalidate the internal active source record when goes to standby
         // This set will also update mIsActiveSource
-        mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS);
+        mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
+                "HdmiCecLocalDeviceAudioSystem#onStandby()");
         mTvSystemAudioModeSupport = null;
         // Record the last state of System Audio Control before going to standby
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d675b81..f2f6dbe 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -190,7 +190,8 @@
         boolean wasActiveSource = mIsActiveSource;
         // Invalidate the internal active source record when goes to standby
         // This set will also update mIsActiveSource
-        mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS);
+        mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
+                "HdmiCecLocalDevicePlayback#onStandby()");
         if (initiatedByCec || !mAutoTvOff || !wasActiveSource) {
             return;
         }
@@ -399,7 +400,8 @@
     protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
         assertRunOnServiceThread();
         if (physicalAddress != mService.getPhysicalAddress()) {
-            setActiveSource(physicalAddress);
+            setActiveSource(physicalAddress,
+                    "HdmiCecLocalDevicePlayback#handleRoutingChangeAndInformation()");
             return;
         }
         switch (mPlaybackDeviceActionOnRoutingControl) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 44ad8ee..4ff36c4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -119,11 +119,11 @@
     }
 
     @ServiceThreadOnly
-    protected void setActiveSource(int physicalAddress) {
+    protected void setActiveSource(int physicalAddress, String caller) {
         assertRunOnServiceThread();
         // Invalidate the internal active source record. This will also update mIsActiveSource.
         ActiveSource activeSource = ActiveSource.of(Constants.ADDR_INVALID, physicalAddress);
-        setActiveSource(activeSource);
+        setActiveSource(activeSource, caller);
     }
 
     @ServiceThreadOnly
@@ -133,7 +133,7 @@
         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
         ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
         if (!getActiveSource().equals(activeSource)) {
-            setActiveSource(activeSource);
+            setActiveSource(activeSource, "HdmiCecLocalDeviceSource#handleActiveSource()");
         }
         setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
         updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
@@ -162,7 +162,7 @@
             setAndBroadcastActiveSource(message, physicalAddress);
         }
         if (physicalAddress != mService.getPhysicalAddress()) {
-            setActiveSource(physicalAddress);
+            setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleSetStreamPath()");
         }
         switchInputOnReceivingNewActivePath(physicalAddress);
         return true;
@@ -174,7 +174,7 @@
         assertRunOnServiceThread();
         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams(), 2);
         if (physicalAddress != mService.getPhysicalAddress()) {
-            setActiveSource(physicalAddress);
+            setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleRoutingChange()");
         }
         if (!isRoutingControlFeatureEnabled()) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
@@ -196,7 +196,7 @@
         assertRunOnServiceThread();
         int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
         if (physicalAddress != mService.getPhysicalAddress()) {
-            setActiveSource(physicalAddress);
+            setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleRoutingInformation()");
         }
         if (!isRoutingControlFeatureEnabled()) {
             mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 804cc92..0325ad9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -288,13 +288,14 @@
         if (targetAddress == Constants.ADDR_INTERNAL) {
             handleSelectInternalSource();
             // Switching to internal source is always successful even when CEC control is disabled.
-            setActiveSource(targetAddress, mService.getPhysicalAddress());
+            setActiveSource(targetAddress, mService.getPhysicalAddress(),
+                    "HdmiCecLocalDeviceTv#deviceSelect()");
             setActivePath(mService.getPhysicalAddress());
             invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
             return;
         }
         if (!mService.isControlEnabled()) {
-            setActiveSource(targetDevice);
+            setActiveSource(targetDevice, "HdmiCecLocalDeviceTv#deviceSelect()");
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
@@ -307,7 +308,8 @@
         assertRunOnServiceThread();
         // Seq #18
         if (mService.isControlEnabled() && getActiveSource().logicalAddress != mAddress) {
-            updateActiveSource(mAddress, mService.getPhysicalAddress());
+            updateActiveSource(mAddress, mService.getPhysicalAddress(),
+                    "HdmiCecLocalDeviceTv#handleSelectInternalSource()");
             if (mSkipRoutingControl) {
                 mSkipRoutingControl = false;
                 return;
@@ -319,19 +321,19 @@
     }
 
     @ServiceThreadOnly
-    void updateActiveSource(int logicalAddress, int physicalAddress) {
+    void updateActiveSource(int logicalAddress, int physicalAddress, String caller) {
         assertRunOnServiceThread();
-        updateActiveSource(ActiveSource.of(logicalAddress, physicalAddress));
+        updateActiveSource(ActiveSource.of(logicalAddress, physicalAddress), caller);
     }
 
     @ServiceThreadOnly
-    void updateActiveSource(ActiveSource newActive) {
+    void updateActiveSource(ActiveSource newActive, String caller) {
         assertRunOnServiceThread();
         // Seq #14
         if (getActiveSource().equals(newActive)) {
             return;
         }
-        setActiveSource(newActive);
+        setActiveSource(newActive, caller);
         int logicalAddress = newActive.logicalAddress;
         if (getCecDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) {
             if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 4ad51de..0b4f31d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -293,6 +293,11 @@
         return success ? OK : ERROR_PARAMETER;
     }
 
+    private boolean isWithinRange(int value, int min, int max) {
+        value = value & 0xFF;
+        return (value >= min && value <= max);
+    }
+
     private class PhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0576e91..44b6a63 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1970,7 +1970,21 @@
 
         @Override
         public void powerOnRemoteDevice(int logicalAddress, int powerStatus) {
-            // TODO(amyjojo): implement the method
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    Slog.i(TAG, "Device "
+                            + logicalAddress + " power status is " + powerStatus
+                            + " before power on command sent out");
+                    if (getSwitchDevice() != null) {
+                        getSwitchDevice().sendUserControlPressedAndReleased(
+                                logicalAddress, HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION);
+                    } else {
+                        Slog.e(TAG, "Can't get the correct local device to handle routing.");
+                    }
+                }
+            });
         }
 
         @Override
@@ -3214,7 +3228,7 @@
         }
     }
 
-    void setActiveSource(int logicalAddress, int physicalAddress) {
+    void setActiveSource(int logicalAddress, int physicalAddress, String caller) {
         synchronized (mLock) {
             mActiveSource.logicalAddress = logicalAddress;
             mActiveSource.physicalAddress = physicalAddress;
@@ -3225,14 +3239,17 @@
             // mIsActiveSource only exists in source device, ignore this setting if the current
             // device is not an HdmiCecLocalDeviceSource.
             if (!(device instanceof HdmiCecLocalDeviceSource)) {
+                device.addActiveSourceHistoryItem(new ActiveSource(logicalAddress, physicalAddress),
+                        false, caller);
                 continue;
             }
-            if (logicalAddress == device.getDeviceInfo().getLogicalAddress()
-                && physicalAddress == getPhysicalAddress()) {
-                ((HdmiCecLocalDeviceSource) device).setIsActiveSource(true);
-            } else {
-                ((HdmiCecLocalDeviceSource) device).setIsActiveSource(false);
-            }
+            boolean deviceIsActiveSource =
+                    logicalAddress == device.getDeviceInfo().getLogicalAddress()
+                            && physicalAddress == getPhysicalAddress();
+
+            ((HdmiCecLocalDeviceSource) device).setIsActiveSource(deviceIsActiveSource);
+            device.addActiveSourceHistoryItem(new ActiveSource(logicalAddress, physicalAddress),
+                    deviceIsActiveSource, caller);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3fddd5a..3235b20 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3274,6 +3274,9 @@
 
     boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
+        if (mCurClient == null || mCurClient.curSession == null) {
+            return false;
+        }
         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                 && (mShowExplicitlyRequested || mShowForced)) {
             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
@@ -3458,7 +3461,9 @@
         // pre-rendering not supported on low-ram devices.
         cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam;
 
-        if (mCurFocusedWindow == windowToken) {
+        final boolean sameWindowFocused = mCurFocusedWindow == windowToken;
+        final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
+        if (sameWindowFocused && isTextEditor) {
             if (DEBUG) {
                 Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
                         + " attribute=" + attribute + ", token = " + windowToken
@@ -3473,6 +3478,7 @@
                     InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
                     null, null, null, -1, null);
         }
+
         mCurFocusedWindow = windowToken;
         mCurFocusedWindowSoftInputMode = softInputMode;
         mCurFocusedWindowClient = cs;
@@ -3490,7 +3496,6 @@
                         == LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                 || mRes.getConfiguration().isLayoutSizeAtLeast(
                         Configuration.SCREENLAYOUT_SIZE_LARGE);
-        final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0;
 
         // We want to start input before showing the IME, but after closing
         // it.  We want to do this after closing it to help the IME disappear
@@ -3550,9 +3555,11 @@
                 }
                 break;
             case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
-                if (DEBUG) Slog.v(TAG, "Window asks to hide input");
-                hideCurrentInputLocked(mCurFocusedWindow, 0, null,
-                        SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
+                if (isImeVisible()) {
+                    if (DEBUG) Slog.v(TAG, "Window asks to hide input");
+                    hideCurrentInputLocked(mCurFocusedWindow, 0, null,
+                            SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
+                }
                 break;
             case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
                 if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
@@ -3577,13 +3584,15 @@
                 if (DEBUG) Slog.v(TAG, "Window asks to always show input");
                 if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
                         unverifiedTargetSdkVersion, startInputFlags)) {
-                    if (attribute != null) {
-                        res = startInputUncheckedLocked(cs, inputContext, missingMethods,
-                                attribute, startInputFlags, startInputReason);
-                        didStart = true;
+                    if (!isImeVisible()) {
+                        if (attribute != null) {
+                            res = startInputUncheckedLocked(cs, inputContext, missingMethods,
+                                    attribute, startInputFlags, startInputReason);
+                            didStart = true;
+                        }
+                        showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+                                SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
                     }
-                    showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
-                            SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
                 } else {
                     Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
                             + " there is no focused view that also returns true from"
@@ -3601,6 +3610,10 @@
                 } else {
                     res = InputBindResult.NO_EDITOR;
                 }
+            } else if (sameWindowFocused) {
+                return new InputBindResult(
+                        InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
+                        null, null, null, -1, null);
             } else {
                 res = InputBindResult.NULL_EDITOR_INFO;
             }
@@ -3608,6 +3621,10 @@
         return res;
     }
 
+    private boolean isImeVisible() {
+        return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0;
+    }
+
     private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
         // TODO(yukawa): multi-display support.
         final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/inputmethod/TEST_MAPPING b/services/core/java/com/android/server/inputmethod/TEST_MAPPING
new file mode 100644
index 0000000..0ccd75d
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+    {
+      "path": "frameworks/base/core/java/android/view/inputmethod"
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 817902d..b61c6a7 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -205,8 +205,13 @@
                     }
 
                     if (DEBUG_INTEGRITY_COMPONENT) {
-                        Slog.i(TAG, String.format("Successfully pushed rule set: %s", version));
+                        Slog.i(
+                                TAG,
+                                String.format(
+                                        "Successfully pushed rule set to version '%s' from '%s'",
+                                        version, ruleProvider));
                     }
+
                     FrameworkStatsLog.write(
                             FrameworkStatsLog.INTEGRITY_RULES_PUSHED,
                             success,
@@ -324,13 +329,12 @@
                                 + getAllowedInstallers(packageInfo));
             }
             IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata);
-            if (DEBUG_INTEGRITY_COMPONENT) {
+            if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) {
                 Slog.i(
                         TAG,
-                        "Integrity check result: "
-                                + result.getEffect()
-                                + " due to "
-                                + result.getMatchedRules());
+                        String.format(
+                                "Integrity check of %s result: %s due to %s",
+                                packageName, result.getEffect(), result.getMatchedRules()));
             }
 
             FrameworkStatsLog.write(
@@ -673,8 +677,10 @@
         // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages.
         List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps();
         if (DEBUG_INTEGRITY_COMPONENT) {
-            Slog.i(TAG, String.format(
-                    "Rule provider system app list contains: %s", allowedRuleProviders));
+            Slog.i(
+                    TAG,
+                    String.format(
+                            "Rule provider system app list contains: %s", allowedRuleProviders));
         }
 
         // Identify the package names in the caller list.
@@ -730,9 +736,9 @@
 
     private boolean integrityCheckIncludesRuleProvider() {
         return Settings.Global.getInt(
-                        mContext.getContentResolver(),
-                        Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
-                        0)
+                mContext.getContentResolver(),
+                Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
+                0)
                 == 1;
     }
 
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 71f1833..f4d0a62 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -697,7 +697,8 @@
             return null;
         }
 
-        Location location = manager.getLastLocation(request, identity, permissionLevel);
+        Location location = manager.getLastLocation(identity, permissionLevel,
+                request.isLocationSettingsIgnored());
 
         // lastly - note app ops
         if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
@@ -740,13 +741,9 @@
                 return null;
             }
 
-            // create a location request that works in almost all circumstances
-            LocationRequest request = LocationRequest.createFromDeprecatedProvider(GPS_PROVIDER, 0,
-                    0, true);
-
-            // use our own identity rather than the caller
-            CallerIdentity identity = CallerIdentity.fromContext(mContext);
-            Location location = gpsManager.getLastLocation(request, identity, PERMISSION_FINE);
+            // use fine permission level to avoid creating unnecessary coarse locations
+            Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL,
+                    PERMISSION_FINE, false);
             if (location == null) {
                 return null;
             }
@@ -1133,9 +1130,7 @@
             return;
         }
 
-        String dumpFilter = args.length == 0 ? null : args[0];
-
-        ipw.println("Location Manager State:");
+        ipw.print("Location Manager State:");
         ipw.increaseIndent();
         ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
 
@@ -1166,34 +1161,28 @@
                 ipw.println(
                         "Location Controller Extra Package: " + mExtraLocationControllerPackage
                                 + (mExtraLocationControllerPackageEnabled ? " [enabled]"
-                                : "[disabled]"));
+                                : " [disabled]"));
             }
         }
 
         ipw.println("Location Providers:");
         ipw.increaseIndent();
         for (LocationProviderManager manager : mProviderManagers) {
-            if (dumpFilter == null || manager.getName().equals(dumpFilter)) {
-                manager.dump(fd, ipw, args);
-            }
+            manager.dump(fd, ipw, args);
         }
         ipw.decreaseIndent();
 
-        if (dumpFilter == null || GPS_PROVIDER.equals(dumpFilter)) {
-            if (mGnssManagerService != null) {
-                ipw.println("GNSS Manager:");
-                ipw.increaseIndent();
-                mGnssManagerService.dump(fd, ipw, args);
-                ipw.decreaseIndent();
-            }
-        }
-
-        if (dumpFilter == null || "geofence".equals(dumpFilter)) {
-            ipw.println("Geofence Manager:");
+        if (mGnssManagerService != null) {
+            ipw.println("GNSS Manager:");
             ipw.increaseIndent();
-            mGeofenceManager.dump(fd, ipw, args);
+            mGnssManagerService.dump(fd, ipw, args);
             ipw.decreaseIndent();
         }
+
+        ipw.println("Geofence Manager:");
+        ipw.increaseIndent();
+        mGeofenceManager.dump(fd, ipw, args);
+        ipw.decreaseIndent();
     }
 
     private class LocalService extends LocationManagerInternal {
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 06105bf..1815a85 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -84,7 +84,7 @@
 import com.android.server.PendingIntentUtils;
 import com.android.server.location.LocationPermissions.PermissionLevel;
 import com.android.server.location.listeners.ListenerMultiplexer;
-import com.android.server.location.listeners.RemovableListenerRegistration;
+import com.android.server.location.listeners.RemoteListenerRegistration;
 import com.android.server.location.util.AppForegroundHelper;
 import com.android.server.location.util.AppForegroundHelper.AppForegroundListener;
 import com.android.server.location.util.AppOpsHelper;
@@ -154,15 +154,8 @@
 
         @Override
         public void deliverOnLocationChanged(Location location,
-                @Nullable Runnable onCompleteCallback)
-                throws RemoteException {
-            mListener.onLocationChanged(location,
-                    onCompleteCallback == null ? null : new IRemoteCallback.Stub() {
-                        @Override
-                        public void sendResult(Bundle data) {
-                            onCompleteCallback.run();
-                        }
-                    });
+                @Nullable Runnable onCompleteCallback) throws RemoteException {
+            mListener.onLocationChanged(location, SingleUseCallback.wrap(onCompleteCallback));
         }
 
         @Override
@@ -221,7 +214,7 @@
     }
 
     protected abstract class Registration extends
-            RemovableListenerRegistration<LocationRequest, LocationTransport> {
+            RemoteListenerRegistration<LocationRequest, LocationTransport> {
 
         @PermissionLevel protected final int mPermissionLevel;
         private final WorkSource mWorkSource;
@@ -306,11 +299,12 @@
         }
 
         @Override
-        protected final void onInactive() {
+        protected final ListenerOperation<LocationTransport> onInactive() {
             onHighPowerUsageChanged();
             if (!getRequest().getHideFromAppOps()) {
                 mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
             }
+            return null;
         }
 
         @Override
@@ -440,18 +434,17 @@
             }
 
             LocationRequest newRequest = calculateProviderLocationRequest();
-            if (!mProviderLocationRequest.equals(newRequest)) {
-                LocationRequest oldRequest = mProviderLocationRequest;
-                mProviderLocationRequest = newRequest;
-                onHighPowerUsageChanged();
-                updateService();
-
-                // if location settings ignored has changed then the active state may have changed
-                return oldRequest.isLocationSettingsIgnored()
-                        != newRequest.isLocationSettingsIgnored();
+            if (mProviderLocationRequest.equals(newRequest)) {
+                return false;
             }
 
-            return false;
+            LocationRequest oldRequest = mProviderLocationRequest;
+            mProviderLocationRequest = newRequest;
+            onHighPowerUsageChanged();
+            updateService();
+
+            // if location settings ignored has changed then the active state may have changed
+            return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored();
         }
 
         private LocationRequest calculateProviderLocationRequest() {
@@ -826,6 +819,12 @@
         @GuardedBy("mLock")
         @Override
         protected void onProviderListenerRegister() {
+            try {
+                ((IBinder) getKey()).linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                remove();
+            }
+
             mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(
                     SystemClock.elapsedRealtime());
 
@@ -837,12 +836,6 @@
                         0, this, FgThread.getHandler(), getWorkSource());
             }
 
-            try {
-                ((IBinder) getKey()).linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                remove();
-            }
-
             // start listening for provider enabled/disabled events
             addEnabledListener(this);
 
@@ -1066,8 +1059,13 @@
             mUserInfoHelper.addListener(mUserChangedListener);
             mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
-            // initialize enabled state
-            onUserStarted(UserHandle.USER_ALL);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                // initialize enabled state
+                onUserStarted(UserHandle.USER_ALL);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1077,10 +1075,15 @@
             mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
             // notify and remove all listeners
-            onUserStopped(UserHandle.USER_ALL);
-            removeRegistrationIf(key -> true);
-            mEnabledListeners.clear();
+            long identity = Binder.clearCallingIdentity();
+            try {
+                onUserStopped(UserHandle.USER_ALL);
+                removeRegistrationIf(key -> true);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
 
+            mEnabledListeners.clear();
             mStarted = false;
         }
     }
@@ -1141,14 +1144,26 @@
     public void setRealProvider(AbstractLocationProvider provider) {
         synchronized (mLock) {
             Preconditions.checkState(mStarted);
-            mProvider.setRealProvider(provider);
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mProvider.setRealProvider(provider);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     public void setMockProvider(@Nullable MockProvider provider) {
         synchronized (mLock) {
             Preconditions.checkState(mStarted);
-            mProvider.setMockProvider(provider);
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mProvider.setMockProvider(provider);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
 
             // when removing a mock provider, also clear any mock last locations and reset the
             // location fudger. the mock provider could have been used to infer the current
@@ -1170,7 +1185,12 @@
                 throw new IllegalArgumentException(mName + " provider is not a test provider");
             }
 
-            mProvider.setMockProviderAllowed(enabled);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mProvider.setMockProviderAllowed(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1180,15 +1200,20 @@
                 throw new IllegalArgumentException(mName + " provider is not a test provider");
             }
 
-            String locationProvider = location.getProvider();
-            if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
-                // The location has an explicit provider that is different from the mock
-                // provider name. The caller may be trying to fool us via b/33091107.
-                EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
-                        mName + "!=" + locationProvider);
-            }
+            long identity = Binder.clearCallingIdentity();
+            try {
+                String locationProvider = location.getProvider();
+                if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
+                    // The location has an explicit provider that is different from the mock
+                    // provider name. The caller may be trying to fool us via b/33091107.
+                    EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
+                            mName + "!=" + locationProvider);
+                }
 
-            mProvider.setMockProviderLocation(location);
+                mProvider.setMockProviderLocation(location);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1203,10 +1228,8 @@
     }
 
     @Nullable
-    public Location getLastLocation(LocationRequest request, CallerIdentity identity,
-            @PermissionLevel int permissionLevel) {
-        Preconditions.checkArgument(mName.equals(request.getProvider()));
-
+    public Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel,
+            boolean ignoreLocationSettings) {
         if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
                 identity.getPackageName())) {
             return null;
@@ -1214,12 +1237,12 @@
         if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
             return null;
         }
-        if (!request.isLocationSettingsIgnored() && !isEnabled(identity.getUserId())) {
+        if (!ignoreLocationSettings && !isEnabled(identity.getUserId())) {
             return null;
         }
 
-        Location location = getLastLocation(identity.getUserId(), permissionLevel,
-                request.isLocationSettingsIgnored());
+        Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel,
+                ignoreLocationSettings);
 
         // we don't note op here because we don't know what the client intends to do with the
         // location, the client is responsible for noting if necessary
@@ -1233,9 +1256,30 @@
         }
     }
 
+    /**
+     * This function does not perform any permissions or safety checks, by calling it you are
+     * committing to performing all applicable checks yourself. Prefer
+     * {@link #getLastLocation(CallerIdentity, int, boolean)} where possible.
+     */
     @Nullable
-    private Location getLastLocation(int userId, @PermissionLevel int permissionLevel,
+    public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel,
             boolean ignoreLocationSettings) {
+        if (userId == UserHandle.USER_ALL) {
+            Location lastLocation = null;
+            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            for (int i = 0; i < runningUserIds.length; i++) {
+                Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
+                        ignoreLocationSettings);
+                if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos()
+                        > lastLocation.getElapsedRealtimeNanos())) {
+                    lastLocation = next;
+                }
+            }
+            return lastLocation;
+        }
+
+        Preconditions.checkArgument(userId >= 0);
+
         synchronized (mLock) {
             LastLocation lastLocation = mLastLocations.get(userId);
             if (lastLocation == null) {
@@ -1247,7 +1291,7 @@
 
     public void injectLastLocation(Location location, int userId) {
         synchronized (mLock) {
-            if (getLastLocation(userId, PERMISSION_FINE, false) == null) {
+            if (getLastLocationUnsafe(userId, PERMISSION_FINE, false) == null) {
                 setLastLocation(location, userId);
             }
         }
@@ -1279,7 +1323,7 @@
         }
     }
 
-    public void getCurrentLocation(LocationRequest request, CallerIdentity identity,
+    public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity,
             int permissionLevel, ICancellationSignal cancellationTransport,
             ILocationCallback callback) {
         Preconditions.checkArgument(mName.equals(request.getProvider()));
@@ -1291,12 +1335,27 @@
         GetCurrentLocationListenerRegistration registration =
                 new GetCurrentLocationListenerRegistration(
                         request,
-                        identity,
+                        callerIdentity,
                         new GetCurrentLocationTransport(callback),
                         permissionLevel);
 
         synchronized (mLock) {
-            Location lastLocation = getLastLocation(request, identity, permissionLevel);
+            if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(),
+                    callerIdentity.getPackageName())) {
+                registration.deliverLocation(null);
+                return;
+            }
+            if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) {
+                registration.deliverLocation(null);
+                return;
+            }
+            if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) {
+                registration.deliverLocation(null);
+                return;
+            }
+
+            Location lastLocation = getLastLocationUnsafe(callerIdentity.getUserId(),
+                    permissionLevel, request.isLocationSettingsIgnored());
             if (lastLocation != null) {
                 long locationAgeMs = NANOSECONDS.toMillis(
                         SystemClock.elapsedRealtimeNanos()
@@ -1314,63 +1373,94 @@
             }
 
             // if last location isn't good enough then we add a location request
-            addRegistration(callback.asBinder(), registration);
-            CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
-                    cancellationTransport);
-            if (cancellationSignal != null) {
-                cancellationSignal.setOnCancelListener(
-                        () -> {
-                            synchronized (mLock) {
-                                removeRegistration(callback.asBinder(), registration);
-                            }
-                        });
+            long identity = Binder.clearCallingIdentity();
+            try {
+                addRegistration(callback.asBinder(), registration);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
+
+        CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
+                cancellationTransport);
+        if (cancellationSignal != null) {
+            cancellationSignal.setOnCancelListener(SingleUseCallback.wrap(
+                    () -> {
+                        synchronized (mLock) {
+                            removeRegistration(callback.asBinder(), registration);
+                        }
+                    }));
+        }
     }
 
     public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
-        mProvider.sendExtraCommand(uid, pid, command, extras);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mProvider.sendExtraCommand(uid, pid, command, extras);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
-    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+    public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
             @PermissionLevel int permissionLevel, ILocationListener listener) {
         Preconditions.checkArgument(mName.equals(request.getProvider()));
 
         synchronized (mLock) {
-            addRegistration(
-                    listener.asBinder(),
-                    new LocationListenerRegistration(
-                            request,
-                            identity,
-                            new LocationListenerTransport(listener),
-                            permissionLevel));
+            long identity = Binder.clearCallingIdentity();
+            try {
+                addRegistration(
+                        listener.asBinder(),
+                        new LocationListenerRegistration(
+                                request,
+                                callerIdentity,
+                                new LocationListenerTransport(listener),
+                                permissionLevel));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
-    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+    public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
             @PermissionLevel int permissionLevel, PendingIntent pendingIntent) {
         Preconditions.checkArgument(mName.equals(request.getProvider()));
 
         synchronized (mLock) {
-            addRegistration(
-                    pendingIntent,
-                    new LocationPendingIntentRegistration(
-                            request,
-                            identity,
-                            new LocationPendingIntentTransport(mContext, pendingIntent),
-                            permissionLevel));
+            long identity = Binder.clearCallingIdentity();
+            try {
+                addRegistration(
+                        pendingIntent,
+                        new LocationPendingIntentRegistration(
+                                request,
+                                callerIdentity,
+                                new LocationPendingIntentTransport(mContext, pendingIntent),
+                                permissionLevel));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     public void unregisterLocationRequest(ILocationListener listener) {
         synchronized (mLock) {
-            removeRegistration(listener.asBinder());
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeRegistration(listener.asBinder());
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     public void unregisterLocationRequest(PendingIntent pendingIntent) {
         synchronized (mLock) {
-            removeRegistration(pendingIntent);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeRegistration(pendingIntent);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1877,7 +1967,8 @@
                     ipw.println("user " + userId + ":");
                     ipw.increaseIndent();
                 }
-                ipw.println("last location=" + getLastLocation(userId, PERMISSION_FINE, false));
+                ipw.println(
+                        "last location=" + getLastLocationUnsafe(userId, PERMISSION_FINE, false));
                 ipw.println("enabled=" + isEnabled(userId));
                 if (userIds.length != 1) {
                     ipw.decreaseIndent();
@@ -1950,7 +2041,7 @@
             }
             // update last coarse interval only if enough time has passed
             long timeDeltaMs = NANOSECONDS.toMillis(newCoarse.getElapsedRealtimeNanos())
-                        - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos());
+                    - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos());
             if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) {
                 return newCoarse;
             } else {
@@ -1958,4 +2049,61 @@
             }
         }
     }
+
+    private static class SingleUseCallback extends IRemoteCallback.Stub implements Runnable,
+            CancellationSignal.OnCancelListener {
+
+        @Nullable
+        public static SingleUseCallback wrap(@Nullable Runnable callback) {
+            return callback == null ? null : new SingleUseCallback(callback);
+        }
+
+        @GuardedBy("this")
+        @Nullable private Runnable mCallback;
+
+        private SingleUseCallback(Runnable callback) {
+            mCallback = Objects.requireNonNull(callback);
+        }
+
+        @Override
+        public void sendResult(Bundle data) {
+            run();
+        }
+
+        @Override
+        public void onCancel() {
+            run();
+        }
+
+        @Override
+        public void run() {
+            Runnable callback;
+            synchronized (this) {
+                callback = mCallback;
+                mCallback = null;
+            }
+
+            // prevent this callback from being run more than once - otherwise this could provide an
+            // attack vector for a malicious app to break assumptions on how many times a callback
+            // may be invoked, and thus crash system server.
+            if (callback == null) {
+                return;
+            }
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                callback.run();
+            } catch (RuntimeException e) {
+                // since this is within a oneway binder transaction there is nowhere
+                // for exceptions to go - move onto another thread to crash system
+                // server so we find out about it
+                FgThread.getExecutor().execute(() -> {
+                    throw new AssertionError(e);
+                });
+                throw e;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 2d9734e..2d7f028 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -32,6 +32,7 @@
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.WorkSource;
@@ -291,17 +292,28 @@
             @Nullable String attributionTag) {
         LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);
 
-        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
-                AppOpsManager.toReceiverId(pendingIntent));
-        addRegistration(new GeofenceKey(pendingIntent, geofence),
-                new GeofenceRegistration(geofence, identity, pendingIntent));
+        CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName,
+                attributionTag, AppOpsManager.toReceiverId(pendingIntent));
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            addRegistration(new GeofenceKey(pendingIntent, geofence),
+                    new GeofenceRegistration(geofence, callerIdentity, pendingIntent));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
      * Removes the geofence associated with the PendingIntent.
      */
     public void removeGeofence(PendingIntent pendingIntent) {
-        removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
+        long identity = Binder.clearCallingIdentity();
+        try {
+            removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 1b599b0..a9fdacc 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -25,6 +25,7 @@
 import android.location.LocationManagerInternal;
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Process;
@@ -218,16 +219,27 @@
     /**
      * Adds a listener with the given identity and request.
      */
-    protected void addListener(TRequest request, CallerIdentity identity, TListener listener) {
-        addRegistration(listener.asBinder(),
-                new GnssListenerRegistration(request, identity, listener));
+    protected void addListener(TRequest request, CallerIdentity callerIdentity,
+            TListener listener) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            addRegistration(listener.asBinder(),
+                    new GnssListenerRegistration(request, callerIdentity, listener));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
      * Removes the given listener.
      */
     public void removeListener(TListener listener) {
-        removeRegistration(listener.asBinder());
+        long identity = Binder.clearCallingIdentity();
+        try {
+            removeRegistration(listener.asBinder());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 850cf7f..a4486d7 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -85,7 +85,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A GNSS implementation of LocationProvider used by LocationManager.
@@ -181,7 +183,6 @@
     private static final int INJECT_NTP_TIME = 5;
     // PSDS stands for Predicted Satellite Data Service
     private static final int DOWNLOAD_PSDS_DATA = 6;
-    private static final int DOWNLOAD_PSDS_DATA_FINISHED = 11;
     private static final int INITIALIZE_HANDLER = 13;
     private static final int REQUEST_LOCATION = 16;
     private static final int REPORT_LOCATION = 17; // HAL reports location
@@ -288,6 +289,7 @@
     private static final long DOWNLOAD_PSDS_DATA_TIMEOUT_MS = 60 * 1000;
     private static final long WAKELOCK_TIMEOUT_MILLIS = 30 * 1000;
 
+    @GuardedBy("mLock")
     private final ExponentialBackOff mPsdsBackOff = new ExponentialBackOff(RETRY_INTERVAL,
             MAX_RETRY_INTERVAL);
 
@@ -297,14 +299,8 @@
 
     private boolean mShutdown;
 
-    // states for injecting ntp and downloading psds data
-    private static final int STATE_PENDING_NETWORK = 0;
-    private static final int STATE_DOWNLOADING = 1;
-    private static final int STATE_IDLE = 2;
-
-    // flags to trigger NTP or PSDS data download when network becomes available
-    // initialized to true so we do NTP and PSDS when the network comes up after booting
-    private int mDownloadPsdsDataPending = STATE_PENDING_NETWORK;
+    @GuardedBy("mLock")
+    private Set<Integer> mPendingDownloadPsdsTypes = new HashSet<>();
 
     // true if GPS is navigating
     private boolean mNavigating;
@@ -610,6 +606,8 @@
         mNIHandler = new GpsNetInitiatedHandler(context,
                 mNetInitiatedListener,
                 mSuplEsEnabled);
+        // Trigger PSDS data download when the network comes up after booting.
+        mPendingDownloadPsdsTypes.add(GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX);
         mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
                 GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler);
 
@@ -670,10 +668,13 @@
      */
     private void onNetworkAvailable() {
         mNtpTimeHelper.onNetworkAvailable();
-        if (mDownloadPsdsDataPending == STATE_PENDING_NETWORK) {
-            if (mSupportsPsds) {
-                // Download only if supported, (prevents an unnecessary on-boot download)
-                psdsDownloadRequest(/* psdsType= */ GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX);
+        // Download only if supported, (prevents an unnecessary on-boot download)
+        if (mSupportsPsds) {
+            synchronized (mLock) {
+                for (int psdsType : mPendingDownloadPsdsTypes) {
+                    downloadPsdsData(psdsType);
+                }
+                mPendingDownloadPsdsTypes.clear();
             }
         }
     }
@@ -799,17 +800,13 @@
             Log.d(TAG, "handleDownloadPsdsData() called when PSDS not supported");
             return;
         }
-        if (mDownloadPsdsDataPending == STATE_DOWNLOADING) {
-            // already downloading data
-            return;
-        }
         if (!mNetworkConnectivityHandler.isDataNetworkConnected()) {
             // try again when network is up
-            mDownloadPsdsDataPending = STATE_PENDING_NETWORK;
+            synchronized (mLock) {
+                mPendingDownloadPsdsTypes.add(psdsType);
+            }
             return;
         }
-        mDownloadPsdsDataPending = STATE_DOWNLOADING;
-
         synchronized (mLock) {
             // hold wake lock while task runs
             mDownloadPsdsWakeLock.acquire(DOWNLOAD_PSDS_DATA_TIMEOUT_MS);
@@ -820,20 +817,24 @@
                     mGnssConfiguration.getProperties());
             byte[] data = psdsDownloader.downloadPsdsData(psdsType);
             if (data != null) {
-                if (DEBUG) Log.d(TAG, "calling native_inject_psds_data");
-                native_inject_psds_data(data, data.length);
-                mPsdsBackOff.reset();
-            }
-
-            sendMessage(DOWNLOAD_PSDS_DATA_FINISHED, 0, null);
-
-            if (data == null) {
-                // try again later
-                // since this is delayed and not urgent we do not hold a wake lock here
-                // the arg2 below should not be 1 otherwise the wakelock will be under-locked.
+                mHandler.post(() -> {
+                    if (DEBUG) Log.d(TAG, "calling native_inject_psds_data");
+                    native_inject_psds_data(data, data.length, psdsType);
+                    synchronized (mLock) {
+                        mPsdsBackOff.reset();
+                    }
+                });
+            } else {
+                // Try download PSDS data again later according to backoff time.
+                // Since this is delayed and not urgent, we do not hold a wake lock here.
+                // The arg2 below should not be 1 otherwise the wakelock will be under-locked.
+                long backoffMillis;
+                synchronized (mLock) {
+                    backoffMillis = mPsdsBackOff.nextBackoffMillis();
+                }
                 mHandler.sendMessageDelayed(
                         mHandler.obtainMessage(DOWNLOAD_PSDS_DATA, psdsType, 0, null),
-                        mPsdsBackOff.nextBackoffMillis());
+                        backoffMillis);
             }
 
             // Release wake lock held by task, synchronize on mLock in case multiple
@@ -1128,7 +1129,7 @@
                 requestUtcTime();
             } else if ("force_psds_injection".equals(command)) {
                 if (mSupportsPsds) {
-                    psdsDownloadRequest(/* psdsType= */
+                    downloadPsdsData(/* psdsType= */
                             GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX);
                 }
             } else {
@@ -1581,8 +1582,8 @@
         reportLocation(locations);
     }
 
-    void psdsDownloadRequest(int psdsType) {
-        if (DEBUG) Log.d(TAG, "psdsDownloadRequest. psdsType: " + psdsType);
+    void downloadPsdsData(int psdsType) {
+        if (DEBUG) Log.d(TAG, "downloadPsdsData. psdsType: " + psdsType);
         sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
     }
 
@@ -1896,9 +1897,6 @@
                 case DOWNLOAD_PSDS_DATA:
                     handleDownloadPsdsData(msg.arg1);
                     break;
-                case DOWNLOAD_PSDS_DATA_FINISHED:
-                    mDownloadPsdsDataPending = STATE_IDLE;
-                    break;
                 case INITIALIZE_HANDLER:
                     handleInitialize();
                     break;
@@ -2007,8 +2005,6 @@
                 return "REQUEST_LOCATION";
             case DOWNLOAD_PSDS_DATA:
                 return "DOWNLOAD_PSDS_DATA";
-            case DOWNLOAD_PSDS_DATA_FINISHED:
-                return "DOWNLOAD_PSDS_DATA_FINISHED";
             case INITIALIZE_HANDLER:
                 return "INITIALIZE_HANDLER";
             case REPORT_LOCATION:
@@ -2022,6 +2018,21 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        boolean dumpAll = false;
+
+        int opti = 0;
+        while (opti < args.length) {
+            String opt = args[opti];
+            if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
+                break;
+            }
+            opti++;
+            if ("-a".equals(opt)) {
+                dumpAll = true;
+                break;
+            }
+        }
+
         StringBuilder s = new StringBuilder();
         s.append("mStarted=").append(mStarted).append("   (changed ");
         TimeUtils.formatDuration(SystemClock.elapsedRealtime()
@@ -2053,9 +2064,11 @@
             s.append("]\n");
         }
         s.append(mGnssMetrics.dumpGnssMetricsAsText());
-        s.append("native internal state: \n");
-        s.append("  ").append(native_get_internal_state());
-        s.append("\n");
+        if (dumpAll) {
+            s.append("native internal state: \n");
+            s.append("  ").append(native_get_internal_state());
+            s.append("\n");
+        }
         pw.append(s);
     }
 
@@ -2094,7 +2107,7 @@
 
     private native boolean native_supports_psds();
 
-    private native void native_inject_psds_data(byte[] data, int length);
+    private native void native_inject_psds_data(byte[] data, int length, int psdsType);
 
     // DEBUG Support
     private native String native_get_internal_state();
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 8e81f29..2bf6af2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -519,7 +519,7 @@
 
     @Override
     public void psdsDownloadRequest(int psdsType) {
-        mGnssLocationProvider.psdsDownloadRequest(psdsType);
+        mGnssLocationProvider.downloadPsdsData(psdsType);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
index bd8bce8..58aabda 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -32,10 +32,10 @@
  * @param <TListener> listener type
  */
 public abstract class BinderListenerRegistration<TRequest, TListener> extends
-        RemovableListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
+        RemoteListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
 
     /**
-     * Interface to allowed binder retrieval when keys are not themselves IBinder.
+     * Interface to allow binder retrieval when keys are not themselves IBinders.
      */
     public interface BinderKey {
         /**
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f94de9b..8a6b8aa 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -18,12 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Binder;
 import android.os.Build;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
-import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
@@ -31,8 +28,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -42,7 +41,7 @@
  * divided into two categories, active registrations and inactive registrations, as defined by
  * {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
  * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
- * whose active state may have changed.
+ * whose active state may have changed. Listeners will only be invoked for active registrations.
  *
  * Callbacks invoked for various changes will always be ordered according to this lifecycle list:
  *
@@ -64,14 +63,6 @@
  * {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This
  * ensures re-entrant removal does not accidentally remove the incorrect registration.
  *
- * All callbacks will be invoked with a cleared binder identity.
- *
- * Listeners owned by other processes will be run on a direct executor (and thus while holding a
- * lock). Listeners owned by the same process this multiplexer is in will be run asynchronously (and
- * thus without holding a lock). The underlying assumption is that listeners owned by other
- * processes will simply be forwarding the call to those other processes and perhaps performing
- * simple bookkeeping, with no potential for deadlock.
- *
  * @param <TKey>           key type
  * @param <TRequest>       request type
  * @param <TListener>      listener type
@@ -149,46 +140,51 @@
     }
 
     /**
-     * Invoked before the first registration occurs. This is a convenient entry point for
-     * registering listeners, etc, which only need to be present while there are any registrations.
+     * Invoked when the multiplexer goes from having no registrations to having some registrations.
+     * This is a convenient entry point for registering listeners, etc, which only need to be
+     * present while there are any registrations. Invoked while holding the multiplexer's internal
+     * lock.
      */
     protected void onRegister() {}
 
     /**
-     * Invoked after the last unregistration occurs. This is a convenient entry point for
-     * unregistering listeners, etc, which only need to be present while there are any
-     * registrations.
+     * Invoked when the multiplexer goes from having some registrations to having no registrations.
+     * This is a convenient entry point for unregistering listeners, etc, which only need to be
+     * present while there are any registrations. Invoked while holding the multiplexer's internal
+     * lock.
      */
     protected void onUnregister() {}
 
     /**
-     * Invoked when a registration is added.
+     * Invoked when a registration is added. Invoked while holding the multiplexer's internal lock.
      */
     protected void onRegistrationAdded(@NonNull TKey key, @NonNull TRegistration registration) {}
 
     /**
-     * Invoked when a registration is removed.
+     * Invoked when a registration is removed. Invoked while holding the multiplexer's internal
+     * lock.
      */
     protected void onRegistrationRemoved(@NonNull TKey key, @NonNull TRegistration registration) {}
 
     /**
-     * Invoked when the manager goes from having no active registrations to having some active
+     * Invoked when the multiplexer goes from having no active registrations to having some active
      * registrations. This is a convenient entry point for registering listeners, etc, which only
-     * need to be present while there are active registrations.
+     * need to be present while there are active registrations. Invoked while holding the
+     * multiplexer's internal lock.
      */
     protected void onActive() {}
 
     /**
-     * Invoked when the manager goes from having some active registrations to having no active
+     * Invoked when the multiplexer goes from having some active registrations to having no active
      * registrations. This is a convenient entry point for unregistering listeners, etc, which only
-     * need to be present while there are active registrations.
+     * need to be present while there are active registrations. Invoked while holding the
+     * multiplexer's internal lock.
      */
     protected void onInactive() {}
 
     /**
-     * Adds a new registration with the given key. Registration may fail if
-     * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the registration
-     * will not be added. This method cannot be called to add a registration re-entrantly.
+     * Adds a new registration with the given key. This method cannot be called to add a
+     * registration re-entrantly.
      */
     protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
         Objects.requireNonNull(key);
@@ -204,7 +200,6 @@
             // involve removing a prior registration. note that try-with-resources ordering is
             // meaningful here as well. we want to close the reentrancy guard first, as this may
             // generate additional service updates, then close the update service buffer.
-            long identity = Binder.clearCallingIdentity();
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
@@ -224,16 +219,13 @@
                 registration.onRegister(key);
                 onRegistrationAdded(key, registration);
                 onRegistrationActiveChanged(registration);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
 
     /**
-     * Removes the registration with the given key. If unregistration occurs,
-     * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method
-     * cannot be called to remove a registration re-entrantly.
+     * Removes the registration with the given key. This method cannot be called to remove a
+     * registration re-entrantly.
      */
     protected final void removeRegistration(@NonNull Object key) {
         synchronized (mRegistrations) {
@@ -250,9 +242,8 @@
     }
 
     /**
-     * Removes all registrations with keys that satisfy the given predicate. If unregistration
-     * occurs, {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This
-     * method cannot be called to remove a registration re-entrantly.
+     * Removes all registrations with keys that satisfy the given predicate. This method cannot be
+     * called to remove a registration re-entrantly.
      */
     protected final void removeRegistrationIf(@NonNull Predicate<TKey> predicate) {
         synchronized (mRegistrations) {
@@ -281,11 +272,8 @@
 
     /**
      * Removes the given registration with the given key. If the given key has a different
-     * registration at the time this method is called, nothing happens. If unregistration occurs,
-     * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method
-     * allows for re-entrancy, and may be called to remove a registration re-entrantly. In this case
-     * the registration will immediately be marked inactive and unregistered, and will be removed
-     * completely at some later time.
+     * registration at the time this method is called, nothing happens. This method allows for
+     * re-entrancy, and may be called to remove a registration re-entrantly.
      */
     protected final void removeRegistration(@NonNull Object key,
             @NonNull ListenerRegistration<?, ?> registration) {
@@ -324,7 +312,6 @@
         // in multiple service updates. note that try-with-resources ordering is meaningful here as
         // well. we want to close the reentrancy guard first, as this may generate additional
         // service updates, then close the update service buffer.
-        long identity = Binder.clearCallingIdentity();
         try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
              ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
@@ -337,8 +324,6 @@
                     onUnregister();
                 }
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -362,34 +347,42 @@
                 }
             }
 
-            long identity = Binder.clearCallingIdentity();
-            try {
-                if (actives.isEmpty()) {
+            if (actives.isEmpty()) {
+                mCurrentRequest = null;
+                if (mServiceRegistered) {
+                    mServiceRegistered = false;
                     mCurrentRequest = null;
-                    if (mServiceRegistered) {
-                        mServiceRegistered = false;
-                        mCurrentRequest = null;
-                        unregisterWithService();
-                    }
-                    return;
+                    unregisterWithService();
                 }
-
-                TMergedRequest merged = mergeRequests(actives);
-                if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
-                    if (mServiceRegistered) {
-                        mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
-                    } else {
-                        mServiceRegistered = registerWithService(merged);
-                    }
-                    if (mServiceRegistered) {
-                        mCurrentRequest = merged;
-                    } else {
-                        mCurrentRequest = null;
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+                return;
             }
+
+            TMergedRequest merged = mergeRequests(actives);
+            if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
+                if (mServiceRegistered) {
+                    mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
+                } else {
+                    mServiceRegistered = registerWithService(merged);
+                }
+                if (mServiceRegistered) {
+                    mCurrentRequest = merged;
+                } else {
+                    mCurrentRequest = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * Clears currently stored service state, and invokes {@link #updateService()} to force a new
+     * call to {@link #registerWithService(Object)} if necessary. This is useful, for instance, if
+     * the backing service has crashed or otherwise lost state, and needs to be re-initialized.
+     */
+    protected final void resetService() {
+        synchronized (mRegistrations) {
+            mServiceRegistered = false;
+            mCurrentRequest = null;
+            updateService();
         }
     }
 
@@ -404,9 +397,9 @@
 
     /**
      * Evaluates the predicate on all registrations. The predicate should return true if the active
-     * state of the registration may have changed as a result. Any {@link #updateService()}
-     * invocations made while this method is executing will be deferred until after the method is
-     * complete so as to avoid redundant work.
+     * state of the registration may have changed as a result. If the active state of any
+     * registration has changed, {@link #updateService()} will automatically be invoked to handle
+     * the resulting changes.
      */
     protected final void updateRegistrations(@NonNull Predicate<TRegistration> predicate) {
         synchronized (mRegistrations) {
@@ -415,7 +408,6 @@
             // callbacks. note that try-with-resources ordering is meaningful here as well. we want
             // to close the reentrancy guard first, as this may generate additional service updates,
             // then close the update service buffer.
-            long identity = Binder.clearCallingIdentity();
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
@@ -426,8 +418,6 @@
                         onRegistrationActiveChanged(registration);
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
@@ -450,7 +440,10 @@
                     execute(registration, operation);
                 }
             } else {
-                registration.onInactive();
+                ListenerOperation<TListener> operation = registration.onInactive();
+                if (operation != null) {
+                    execute(registration, operation);
+                }
                 if (--mActiveRegistrationsCount == 0) {
                     onInactive();
                 }
@@ -468,7 +461,6 @@
     protected final void deliverToListeners(
             @NonNull Function<TRegistration, ListenerOperation<TListener>> function) {
         synchronized (mRegistrations) {
-            long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
                 final int size = mRegistrations.size();
                 for (int i = 0; i < size; i++) {
@@ -480,8 +472,6 @@
                         }
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
@@ -495,7 +485,6 @@
      */
     protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) {
         synchronized (mRegistrations) {
-            long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
                 final int size = mRegistrations.size();
                 for (int i = 0; i < size; i++) {
@@ -504,8 +493,6 @@
                         execute(registration, operation);
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
@@ -522,27 +509,26 @@
     /**
      * Dumps debug information.
      */
-    public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mRegistrations) {
-            ipw.print("service: ");
-            dumpServiceState(ipw);
-            ipw.println();
+            pw.print("service: ");
+            dumpServiceState(pw);
+            pw.println();
 
             if (!mRegistrations.isEmpty()) {
-                ipw.println("listeners:");
+                pw.println("listeners:");
 
-                ipw.increaseIndent();
                 final int size = mRegistrations.size();
                 for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
-                    ipw.print(registration);
+                    pw.print("  ");
+                    pw.print(registration);
                     if (!registration.isActive()) {
-                        ipw.println(" (inactive)");
+                        pw.println(" (inactive)");
                     } else {
-                        ipw.println();
+                        pw.println();
                     }
                 }
-                ipw.decreaseIndent();
             }
         }
     }
@@ -577,7 +563,7 @@
         @GuardedBy("mRegistrations")
         private int mGuardCount;
         @GuardedBy("mRegistrations")
-        private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
+        private @Nullable ArraySet<Entry<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
 
         ReentrancyGuard() {
             mGuardCount = 0;
@@ -602,7 +588,7 @@
             if (mScheduledRemovals == null) {
                 mScheduledRemovals = new ArraySet<>(mRegistrations.size());
             }
-            mScheduledRemovals.add(new Pair<>(key, registration));
+            mScheduledRemovals.add(new AbstractMap.SimpleImmutableEntry<>(key, registration));
         }
 
         ReentrancyGuard acquire() {
@@ -612,7 +598,7 @@
 
         @Override
         public void close() {
-            ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
+            ArraySet<Entry<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
 
             Preconditions.checkState(mGuardCount > 0);
             if (--mGuardCount == 0) {
@@ -620,14 +606,15 @@
                 mScheduledRemovals = null;
             }
 
-            if (scheduledRemovals != null) {
-                try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
-                    final int size = scheduledRemovals.size();
-                    for (int i = 0; i < size; i++) {
-                        Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
-                                i);
-                        removeRegistration(pair.first, pair.second);
-                    }
+            if (scheduledRemovals == null) {
+                return;
+            }
+
+            try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
+                final int size = scheduledRemovals.size();
+                for (int i = 0; i < size; i++) {
+                    Entry<Object, ListenerRegistration<?, ?>> entry = scheduledRemovals.valueAt(i);
+                    removeRegistration(entry.getKey(), entry.getValue());
                 }
             }
         }
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index ac56c51..deb9660 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -17,55 +17,34 @@
 package com.android.server.location.listeners;
 
 
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.location.util.identity.CallerIdentity;
-import android.os.Process;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.listeners.ListenerExecutor;
-import com.android.server.FgThread;
 
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
  * A listener registration object which holds data associated with the listener, such as an optional
- * request, and the identity of the listener owner.
+ * request, and an executor responsible for listener invocations.
  *
  * @param <TRequest>  request type
  * @param <TListener> listener type
  */
 public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
 
-    @VisibleForTesting
-    public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
-
     private final Executor mExecutor;
     private final @Nullable TRequest mRequest;
-    private final CallerIdentity mIdentity;
 
     private boolean mActive;
 
     private volatile @Nullable TListener mListener;
 
-    protected ListenerRegistration(@Nullable TRequest request, CallerIdentity identity,
+    protected ListenerRegistration(Executor executor, @Nullable TRequest request,
             TListener listener) {
-        // if a client is in the same process as us, binder calls will execute synchronously and
-        // we shouldn't run callbacks directly since they might be run under lock and deadlock
-        if (identity.getPid() == Process.myPid()) {
-            // there's a slight loophole here for pending intents - pending intent callbacks can
-            // always be run on the direct executor since they're always asynchronous, but honestly
-            // you shouldn't be using pending intent callbacks within the same process anyways
-            mExecutor = IN_PROCESS_EXECUTOR;
-        } else {
-            mExecutor = DIRECT_EXECUTOR;
-        }
-
+        mExecutor = Objects.requireNonNull(executor);
         mRequest = request;
-        mIdentity = Objects.requireNonNull(identity);
         mActive = false;
         mListener = Objects.requireNonNull(listener);
     }
@@ -82,34 +61,34 @@
     }
 
     /**
-     * Returns the listener identity.
-     */
-    public final CallerIdentity getIdentity() {
-        return mIdentity;
-    }
-
-    /**
-     * May be overridden by subclasses. Invoked when registration occurs.
+     * May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the
+     * owning multiplexer's internal lock.
      */
     protected void onRegister(Object key) {}
 
     /**
-     * May be overridden by subclasses. Invoked when unregistration occurs.
+     * May be overridden by subclasses. Invoked when unregistration occurs. Invoked while holding
+     * the owning multiplexer's internal lock.
      */
     protected void onUnregister() {}
 
     /**
      * May be overridden by subclasses. Invoked when this registration becomes active. If this
-     * returns a non-null operation, that operation will be invoked for the listener.
+     * returns a non-null operation, that operation will be invoked for the listener. Invoked
+     * while holding the owning multiplexer's internal lock.
      */
     protected @Nullable ListenerOperation<TListener> onActive() {
         return null;
     }
 
     /**
-     * May be overridden by subclasses. Invoked when registration becomes inactive.
+     * May be overridden by subclasses. Invoked when registration becomes inactive. If this returns
+     * a non-null operation, that operation will be invoked for the listener. Invoked while holding
+     * the owning multiplexer's internal lock.
      */
-    protected void onInactive() {}
+    protected @Nullable ListenerOperation<TListener> onInactive() {
+        return null;
+    }
 
     public final boolean isActive() {
         return mActive;
@@ -136,8 +115,7 @@
     /**
      * May be overridden by subclasses, however should rarely be needed. Invoked when the listener
      * associated with this registration is unregistered, which may occur before the registration
-     * itself is unregistered. This immediately prevents the listener from being further invoked
-     * even if the various bookkeeping associated with unregistration has not occurred yet.
+     * itself is unregistered. This immediately prevents the listener from being further invoked.
      */
     protected void onListenerUnregister() {};
 
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index b5d2ef6..7b6154e 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -30,7 +30,7 @@
  * @param <TListener> listener type
  */
 public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends
-        RemovableListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
+        RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
 
     /**
      * Interface to allowed pending intent retrieval when keys are not themselves PendingIntents.
diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
new file mode 100644
index 0000000..e4b0b19
--- /dev/null
+++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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.location.listeners;
+
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import android.annotation.Nullable;
+import android.location.util.identity.CallerIdentity;
+import android.os.Process;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener registration representing a remote (possibly from a different process) listener.
+ * Listeners from a different process will be run on a direct executor, since the x-process listener
+ * invocation should already be asynchronous. Listeners from the same process will be run on a
+ * normal executor, since in-process listener invocation may be synchronous.
+ *
+ * @param <TRequest>  request type
+ * @param <TListener> listener type
+ */
+public abstract class RemoteListenerRegistration<TRequest, TListener> extends
+        RemovableListenerRegistration<TRequest, TListener> {
+
+    @VisibleForTesting
+    public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
+
+    private static Executor chooseExecutor(CallerIdentity identity) {
+        // if a client is in the same process as us, binder calls will execute synchronously and
+        // we shouldn't run callbacks directly since they might be run under lock and deadlock
+        if (identity.getPid() == Process.myPid()) {
+            // there's a slight loophole here for pending intents - pending intent callbacks can
+            // always be run on the direct executor since they're always asynchronous, but honestly
+            // you shouldn't be using pending intent callbacks within the same process anyways
+            return IN_PROCESS_EXECUTOR;
+        } else {
+            return DIRECT_EXECUTOR;
+        }
+    }
+
+    private final CallerIdentity mIdentity;
+
+    protected RemoteListenerRegistration(String tag, @Nullable TRequest request,
+            CallerIdentity identity, TListener listener) {
+        super(tag, chooseExecutor(identity), request, listener);
+        mIdentity = Objects.requireNonNull(identity);
+    }
+
+    /**
+     * Returns the listener identity.
+     */
+    public final CallerIdentity getIdentity() {
+        return mIdentity;
+    }
+}
+
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index 0698cca..2383bec 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -17,10 +17,10 @@
 package com.android.server.location.listeners;
 
 import android.annotation.Nullable;
-import android.location.util.identity.CallerIdentity;
 import android.util.Log;
 
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * A listener registration that stores its own key, and thus can remove itself. By default it will
@@ -36,9 +36,9 @@
 
     private volatile @Nullable Object mKey;
 
-    protected RemovableListenerRegistration(String tag, @Nullable TRequest request,
-            CallerIdentity callerIdentity, TListener listener) {
-        super(request, callerIdentity, listener);
+    protected RemovableListenerRegistration(String tag, Executor executor,
+            @Nullable TRequest request, TListener listener) {
+        super(executor, request, listener);
         mTag = Objects.requireNonNull(tag);
     }
 
@@ -70,7 +70,7 @@
 
     @Override
     public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) {
-        Log.w(mTag, "registration " + getIdentity() + " removed due to unexpected exception", e);
+        Log.w(mTag, "registration " + this + " removed due to unexpected exception", e);
         remove();
     }
 
diff --git a/services/core/java/com/android/server/location/util/SystemSettingsHelper.java b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
index ff4ba91..39aeaba 100644
--- a/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java
@@ -29,6 +29,7 @@
 import static com.android.server.location.LocationManagerService.TAG;
 
 import android.app.ActivityManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -330,11 +331,13 @@
     @Override
     public float getCoarseLocationAccuracyM() {
         long identity = Binder.clearCallingIdentity();
+        final ContentResolver cr = mContext.getContentResolver();
         try {
-            return Settings.Secure.getFloat(
-                    mContext.getContentResolver(),
+            return Settings.Secure.getFloatForUser(
+                    cr,
                     LOCATION_COARSE_ACCURACY_M,
-                    DEFAULT_COARSE_LOCATION_ACCURACY_M);
+                    DEFAULT_COARSE_LOCATION_ACCURACY_M,
+                    cr.getUserId());
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 26c3132..dbc725e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -837,7 +837,7 @@
         if (getString("migrated", null, 0) == null) {
             final ContentResolver cr = mContext.getContentResolver();
             for (String validSetting : VALID_SETTINGS) {
-                String value = Settings.Secure.getString(cr, validSetting);
+                String value = Settings.Secure.getStringForUser(cr, validSetting, cr.getUserId());
                 if (value != null) {
                     setString(validSetting, value, 0);
                 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index e9a05a8..715e41c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -746,7 +746,7 @@
 
     public void dump(IndentingPrintWriter pw) {
         final UserManager um = UserManager.get(mContext);
-        for (UserInfo user : um.getUsers(false)) {
+        for (UserInfo user : um.getUsers()) {
             File userPath = getSyntheticPasswordDirectoryForUser(user.id);
             pw.println(String.format("User %d [%s]:", user.id, userPath.getAbsolutePath()));
             pw.increaseIndent();
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1e02f49..793cfcd 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -23,7 +23,6 @@
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
-import android.media.AudioManagerInternal;
 import android.media.AudioSystem;
 import android.media.MediaMetadata;
 import android.media.Rating;
@@ -53,8 +52,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
-import com.android.server.LocalServices;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -144,7 +141,6 @@
     // Volume handling fields
     private AudioAttributes mAudioAttrs;
     private AudioManager mAudioManager;
-    private AudioManagerInternal mAudioManagerInternal;
     private int mVolumeType = PlaybackInfo.PLAYBACK_TYPE_LOCAL;
     private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
     private int mMaxVolume = 0;
@@ -179,7 +175,6 @@
         mContext = mService.getContext();
         mHandler = new MessageHandler(handlerLooper);
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mAudioAttrs = DEFAULT_ATTRIBUTES;
         mPolicies = policies;
 
@@ -328,8 +323,9 @@
                 @Override
                 public void run() {
                     try {
-                        mAudioManagerInternal.setStreamVolumeForUid(stream, volumeValue, flags,
-                                opPackageName, uid, pid);
+                        mAudioManager.setStreamVolumeForUid(stream, volumeValue, flags,
+                                opPackageName, uid, pid,
+                                mContext.getApplicationInfo().targetSdkVersion);
                     } catch (IllegalArgumentException | SecurityException e) {
                         Log.e(TAG, "Cannot set volume: stream=" + stream + ", value=" + volumeValue
                                 + ", flags=" + flags, e);
@@ -518,16 +514,19 @@
                 try {
                     if (useSuggested) {
                         if (AudioSystem.isStreamActive(stream, 0)) {
-                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream,
-                                    direction, flags, opPackageName, uid, pid);
+                            mAudioManager.adjustSuggestedStreamVolumeForUid(stream,
+                                    direction, flags, opPackageName, uid, pid,
+                                    mContext.getApplicationInfo().targetSdkVersion);
                         } else {
-                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+                            mAudioManager.adjustSuggestedStreamVolumeForUid(
                                     AudioManager.USE_DEFAULT_STREAM_TYPE, direction,
-                                    flags | previousFlagPlaySound, opPackageName, uid, pid);
+                                    flags | previousFlagPlaySound, opPackageName, uid, pid,
+                                    mContext.getApplicationInfo().targetSdkVersion);
                         }
                     } else {
-                        mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
-                                opPackageName, uid, pid);
+                        mAudioManager.adjustStreamVolumeForUid(stream, direction, flags,
+                                opPackageName, uid, pid,
+                                mContext.getApplicationInfo().targetSdkVersion);
                     }
                 } catch (IllegalArgumentException | SecurityException e) {
                     Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream="
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9521611..d345029 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -42,7 +42,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.media.AudioManager;
-import android.media.AudioManagerInternal;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
 import android.media.IRemoteVolumeController;
@@ -85,7 +84,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.SystemService.TargetUser;
 import com.android.server.Watchdog;
@@ -136,7 +134,7 @@
             new ArrayList<>();
 
     private KeyguardManager mKeyguardManager;
-    private AudioManagerInternal mAudioManagerInternal;
+    private AudioManager mAudioManager;
     private ContentResolver mContentResolver;
     private boolean mHasFeatureLeanback;
 
@@ -162,6 +160,7 @@
         PowerManager pm = mContext.getSystemService(PowerManager.class);
         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
         mNotificationManager = mContext.getSystemService(NotificationManager.class);
+        mAudioManager = mContext.getSystemService(AudioManager.class);
     }
 
     @Override
@@ -169,7 +168,6 @@
         publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
         Watchdog.getInstance().addMonitor(this);
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(mContext);
         mAudioPlayerStateMonitor.registerListener(
                 (config, isRemoved) -> {
@@ -2057,8 +2055,9 @@
                             callingPid = pid;
                         }
                         try {
-                            mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(suggestedStream,
-                                    direction, flags, callingOpPackageName, callingUid, callingPid);
+                            mAudioManager.adjustSuggestedStreamVolumeForUid(suggestedStream,
+                                    direction, flags, callingOpPackageName, callingUid, callingPid,
+                                    getContext().getApplicationInfo().targetSdkVersion);
                         } catch (SecurityException | IllegalArgumentException e) {
                             Log.e(TAG, "Cannot adjust volume: direction=" + direction
                                     + ", suggestedStream=" + suggestedStream + ", flags=" + flags
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 29ee8eb..ded77b3 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -518,8 +518,9 @@
     private final SparseBooleanArray mRestrictBackgroundAllowlistRevokedUids =
             new SparseBooleanArray();
 
+    final Object mMeteredIfacesLock = new Object();
     /** Set of ifaces that are metered. */
-    @GuardedBy("mNetworkPoliciesSecondLock")
+    @GuardedBy("mMeteredIfacesLock")
     private ArraySet<String> mMeteredIfaces = new ArraySet<>();
     /** Set of over-limit templates that have been notified. */
     @GuardedBy("mNetworkPoliciesSecondLock")
@@ -1972,13 +1973,15 @@
         }
 
         // Remove quota from any interfaces that are no longer metered.
-        for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
-            final String iface = mMeteredIfaces.valueAt(i);
-            if (!newMeteredIfaces.contains(iface)) {
-                removeInterfaceQuotaAsync(iface);
+        synchronized (mMeteredIfacesLock) {
+            for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
+                final String iface = mMeteredIfaces.valueAt(i);
+                if (!newMeteredIfaces.contains(iface)) {
+                    removeInterfaceQuotaAsync(iface);
+                }
             }
+            mMeteredIfaces = newMeteredIfaces;
         }
-        mMeteredIfaces = newMeteredIfaces;
 
         final ContentResolver cr = mContext.getContentResolver();
         final boolean quotaEnabled = Settings.Global.getInt(cr,
@@ -2030,7 +2033,10 @@
             mSubscriptionOpportunisticQuota.put(subId, quotaBytes);
         }
 
-        final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
+        final String[] meteredIfaces;
+        synchronized (mMeteredIfacesLock) {
+            meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
+        }
         mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();
 
         mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
@@ -3436,7 +3442,10 @@
                 fout.print("Restrict background: "); fout.println(mRestrictBackground);
                 fout.print("Restrict power: "); fout.println(mRestrictPower);
                 fout.print("Device idle: "); fout.println(mDeviceIdleMode);
-                fout.print("Metered ifaces: "); fout.println(mMeteredIfaces);
+                synchronized (mMeteredIfacesLock) {
+                    fout.print("Metered ifaces: ");
+                    fout.println(mMeteredIfaces);
+                }
 
                 fout.println();
                 fout.print("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -4632,7 +4641,7 @@
                 }
                 case MSG_LIMIT_REACHED: {
                     final String iface = (String) msg.obj;
-                    synchronized (mNetworkPoliciesSecondLock) {
+                    synchronized (mMeteredIfacesLock) {
                         // fast return if not needed.
                         if (!mMeteredIfaces.contains(iface)) {
                             return true;
@@ -5274,7 +5283,7 @@
                 isBackgroundRestricted = mRestrictBackground;
             }
             final boolean isNetworkMetered;
-            synchronized (mNetworkPoliciesSecondLock) {
+            synchronized (mMeteredIfacesLock) {
                 isNetworkMetered = mMeteredIfaces.contains(ifname);
             }
             final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index a604625..74b7bd7 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1554,7 +1554,7 @@
             if (!isEnabledForCurrentProfiles()) {
                 return false;
             }
-            return this.userid == userId;
+            return userId == USER_ALL || userId == this.userid;
         }
 
         public boolean enabledAndUserMatches(int nid) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0465855..8368df9e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -262,7 +262,6 @@
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.UiThread;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LogicalLight;
@@ -966,8 +965,7 @@
                     nv.recycle();
                 }
                 reportUserInteraction(r);
-                mAssistants.notifyAssistantActionClicked(
-                        r.getSbn(), actionIndex, action, generatedByAssistant);
+                mAssistants.notifyAssistantActionClicked(r.getSbn(), action, generatedByAssistant);
             }
         }
 
@@ -1686,8 +1684,10 @@
                 final IntArray userIds = mUserProfiles.getCurrentProfileIds();
 
                 for (int i = 0; i < userIds.size(); i++) {
-                    mArchive.updateHistoryEnabled(userIds.get(i), Settings.Secure.getInt(resolver,
-                            Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0) == 1);
+                    mArchive.updateHistoryEnabled(userIds.get(i),
+                            Settings.Secure.getIntForUser(resolver,
+                                    Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0,
+                                    userIds.get(i)) == 1);
                 }
             }
             if (uri == null || NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI.equals(uri)) {
@@ -6261,6 +6261,8 @@
                     for (NotificationRecord r : enqueued) {
                         if (r.mUpdateTimeMs > mWhen) {
                             // At least one enqueue was posted after the cancel, so we're invalid
+                            Slog.i(TAG, "notification cancel ignored due to newer enqueued entry"
+                                    + "key=" + r.getSbn().getKey());
                             return;
                         }
                     }
@@ -7148,9 +7150,10 @@
     }
 
     protected void playInCallNotification() {
+        final ContentResolver cr = getContext().getContentResolver();
         if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL
-                && Settings.Secure.getInt(getContext().getContentResolver(),
-                Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, 1) != 0) {
+                && Settings.Secure.getIntForUser(cr,
+                Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, 1, cr.getUserId()) != 0) {
             new Thread() {
                 @Override
                 public void run() {
@@ -8629,12 +8632,25 @@
                 ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
     }
 
-    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
+    @VisibleForTesting
+    boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
         if (!listener.enabledAndUserMatches(sbn.getUserId())) {
             return false;
         }
-        // TODO: remove this for older listeners.
-        return true;
+        return isInteractionVisibleToListener(listener, sbn.getUserId());
+    }
+
+    /**
+     * Returns whether the given assistant should be informed about interactions on the given user.
+     *
+     * Normally an assistant would be able to see all interactions on the current user and any
+     * associated profiles because they are notification listeners, but since NASes have one
+     * instance per user, we want to filter out interactions that are not for the user that the
+     * given NAS is bound in.
+     */
+    private boolean isInteractionVisibleToListener(ManagedServiceInfo info, int userId) {
+        boolean isAssistantService = mAssistants.isServiceTokenValidLocked(info.service);
+        return !isAssistantService || info.isSameUser(userId);
     }
 
     private boolean isPackageSuspendedForUser(String pkg, int uid) {
@@ -8856,8 +8872,6 @@
         }
 
         protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) {
-            // There should be only one, but it's a list, so while we enforce
-            // singularity elsewhere, we keep it general here, to avoid surprises.
             for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                 ArrayList<String> keys = new ArrayList<>(records.size());
                 for (NotificationRecord r : records) {
@@ -8875,6 +8889,8 @@
         }
 
         protected void onPanelRevealed(int items) {
+            // send to all currently bounds NASes since notifications from both users will appear in
+            // the panel
             for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                 mHandler.post(() -> {
                     final INotificationListener assistant = (INotificationListener) info.service;
@@ -8888,6 +8904,8 @@
         }
 
         protected void onPanelHidden() {
+            // send to all currently bounds NASes since notifications from both users will appear in
+            // the panel
             for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                 mHandler.post(() -> {
                     final INotificationListener assistant = (INotificationListener) info.service;
@@ -8976,7 +8994,7 @@
             }
             notifyAssistantLocked(
                     sbn,
-                    false /* sameUserOnly */,
+                    true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
                             assistant.onNotificationVisibilityChanged(key, isVisible);
@@ -8994,7 +9012,7 @@
             final String key = sbn.getKey();
             notifyAssistantLocked(
                     sbn,
-                    false /* sameUserOnly */,
+                    true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
                             assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
@@ -9010,7 +9028,7 @@
             final String key = sbn.getKey();
             notifyAssistantLocked(
                     sbn,
-                    false /* sameUserOnly */,
+                    true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
                             assistant.onNotificationDirectReply(key);
@@ -9026,7 +9044,7 @@
             final String key = sbn.getKey();
             notifyAssistantLocked(
                     sbn,
-                    false /* sameUserOnly */,
+                    true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
                             assistant.onSuggestedReplySent(
@@ -9043,12 +9061,12 @@
 
         @GuardedBy("mNotificationLock")
         void notifyAssistantActionClicked(
-                final StatusBarNotification sbn, int actionIndex, Notification.Action action,
+                final StatusBarNotification sbn, Notification.Action action,
                 boolean generatedByAssistant) {
             final String key = sbn.getKey();
             notifyAssistantLocked(
                     sbn,
-                    false /* sameUserOnly */,
+                    true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
                             assistant.onActionClicked(
@@ -9072,7 +9090,7 @@
                 final StatusBarNotification sbn, final String snoozeCriterionId) {
             notifyAssistantLocked(
                     sbn,
-                    false /* sameUserOnly */,
+                    true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
                             assistant.onNotificationSnoozedUntilContext(
@@ -9129,7 +9147,7 @@
         }
 
         protected void resetDefaultAssistantsIfNecessary() {
-            final List<UserInfo> activeUsers = mUm.getUsers(true);
+            final List<UserInfo> activeUsers = mUm.getAliveUsers();
             for (UserInfo userInfo : activeUsers) {
                 int userId = userInfo.getUserHandle().getIdentifier();
                 if (!hasUserSet(userId)) {
@@ -9293,10 +9311,12 @@
         }
 
         public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) {
+            // send to all currently bounds NASes since notifications from both users will appear in
+            // the status bar
             for (final ManagedServiceInfo info : getServices()) {
                 mHandler.post(() -> {
                     final INotificationListener listener = (INotificationListener) info.service;
-                     try {
+                    try {
                         listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons);
                     } catch (RemoteException ex) {
                         Slog.e(TAG, "unable to notify listener "
@@ -9470,7 +9490,8 @@
                     && changedHiddenNotifications.size() > 0;
 
             for (final ManagedServiceInfo serviceInfo : getServices()) {
-                if (!serviceInfo.isEnabledForCurrentProfiles()) {
+                if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
+                        serviceInfo, ActivityManager.getCurrentUser())) {
                     continue;
                 }
 
@@ -9489,12 +9510,7 @@
                     final NotificationRankingUpdate update = makeRankingUpdateLocked(
                             serviceInfo);
 
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            notifyRankingUpdate(serviceInfo, update);
-                        }
-                    });
+                    mHandler.post(() -> notifyRankingUpdate(serviceInfo, update));
                 }
             }
         }
@@ -9502,15 +9518,11 @@
         @GuardedBy("mNotificationLock")
         public void notifyListenerHintsChangedLocked(final int hints) {
             for (final ManagedServiceInfo serviceInfo : getServices()) {
-                if (!serviceInfo.isEnabledForCurrentProfiles()) {
+                if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
+                        serviceInfo, ActivityManager.getCurrentUser())) {
                     continue;
                 }
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyListenerHintsChanged(serviceInfo, hints);
-                    }
-                });
+                mHandler.post(() -> notifyListenerHintsChanged(serviceInfo, hints));
             }
         }
 
@@ -9562,15 +9574,12 @@
 
         public void notifyInterruptionFilterChanged(final int interruptionFilter) {
             for (final ManagedServiceInfo serviceInfo : getServices()) {
-                if (!serviceInfo.isEnabledForCurrentProfiles()) {
+                if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
+                        serviceInfo, ActivityManager.getCurrentUser())) {
                     continue;
                 }
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyInterruptionFilterChanged(serviceInfo, interruptionFilter);
-                    }
-                });
+                mHandler.post(
+                        () -> notifyInterruptionFilterChanged(serviceInfo, interruptionFilter));
             }
         }
 
@@ -9579,15 +9588,16 @@
             if (channel == null) {
                 return;
             }
-            for (final ManagedServiceInfo serviceInfo : getServices()) {
-                if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
+            for (final ManagedServiceInfo info : getServices()) {
+                if (!info.enabledAndUserMatches(UserHandle.getCallingUserId())
+                        || !isInteractionVisibleToListener(info, UserHandle.getCallingUserId())) {
                     continue;
                 }
 
                 BackgroundThread.getHandler().post(() -> {
-                    if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) {
+                    if (info.isSystem || hasCompanionDevice(info)) {
                         notifyNotificationChannelChanged(
-                                serviceInfo, pkg, user, channel, modificationType);
+                                info, pkg, user, channel, modificationType);
                     }
                 });
             }
@@ -9599,15 +9609,16 @@
             if (group == null) {
                 return;
             }
-            for (final ManagedServiceInfo serviceInfo : getServices()) {
-                if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) {
+            for (final ManagedServiceInfo info : getServices()) {
+                if (!info.enabledAndUserMatches(UserHandle.getCallingUserId())
+                        || !isInteractionVisibleToListener(info, UserHandle.getCallingUserId())) {
                     continue;
                 }
 
                 BackgroundThread.getHandler().post(() -> {
-                    if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) {
+                    if (info.isSystem || hasCompanionDevice(info)) {
                         notifyNotificationChannelGroupChanged(
-                                serviceInfo, pkg, user, group, modificationType);
+                                info, pkg, user, group, modificationType);
                     }
                 });
             }
@@ -9626,9 +9637,6 @@
 
         private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
                 NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) {
-            if (!info.enabledAndUserMatches(sbn.getUserId())) {
-                return;
-            }
             final INotificationListener listener = (INotificationListener) info.service;
             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
             try {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index a4debc1..d7a1ba2 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -288,7 +288,7 @@
 
     private void initIfNeeded() {
         final UserManager um = getContext().getSystemService(UserManager.class);
-        final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
+        final List<UserInfo> users = um.getAliveUsers();
         synchronized (mLock) {
             final int userCount = users.size();
             for (int i = 0; i < userCount; i++) {
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
new file mode 100644
index 0000000..0338ed8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
+import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.PackageManager.WHOLE_MD5;
+import static android.content.pm.PackageManager.WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.PackageManager.WHOLE_SHA1;
+import static android.content.pm.PackageManager.WHOLE_SHA256;
+import static android.content.pm.PackageManager.WHOLE_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.FileChecksum;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
+import android.util.apk.ApkSignatureSchemeV3Verifier;
+import android.util.apk.ApkSignatureSchemeV4Verifier;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.apk.ApkSigningBlockUtils;
+import android.util.apk.ByteBufferFactory;
+import android.util.apk.SignatureInfo;
+import android.util.apk.SignatureNotFoundException;
+import android.util.apk.VerityBuilder;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.security.VerityUtils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides checksums for APK.
+ */
+public class ApkChecksums {
+    static final String TAG = "ApkChecksums";
+
+    // MessageDigest algorithms.
+    static final String ALGO_MD5 = "MD5";
+    static final String ALGO_SHA1 = "SHA1";
+    static final String ALGO_SHA256 = "SHA256";
+    static final String ALGO_SHA512 = "SHA512";
+
+    /**
+     * Check back in 1 second after we detected we needed to wait for the APK to be fully available.
+     */
+    private static final long PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS = 1000;
+
+    /**
+     * 24 hours timeout to wait till all files are loaded.
+     */
+    private static final long PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS = 1000 * 3600 * 24;
+
+    /**
+     * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
+     *
+     * NOTE: All getters should return the same instance for every call.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    static class Injector {
+
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+        interface Producer<T> {
+            /** Produce an instance of type {@link T} */
+            T produce();
+        }
+
+        private final Producer<Context> mContext;
+        private final Producer<Handler> mHandlerProducer;
+        private final Producer<IncrementalManager> mIncrementalManagerProducer;
+
+        Injector(Producer<Context> context, Producer<Handler> handlerProducer,
+                Producer<IncrementalManager> incrementalManagerProducer) {
+            mContext = context;
+            mHandlerProducer = handlerProducer;
+            mIncrementalManagerProducer = incrementalManagerProducer;
+        }
+
+        public Context getContext() {
+            return mContext.produce();
+        }
+
+        public Handler getHandler() {
+            return mHandlerProducer.produce();
+        }
+
+        public IncrementalManager getIncrementalManager() {
+            return mIncrementalManagerProducer.produce();
+        }
+    }
+
+    /**
+     * Fetch or calculate checksums for the collection of files.
+     *
+     * @param filesToChecksum   split name, null for base and File to fetch checksums for
+     * @param optional          mask to fetch readily available checksums
+     * @param required          mask to forcefully calculate if not available
+     * @param trustedInstallers array of certificate to trust, two specific cases:
+     *                          null - trust anybody,
+     *                          [] - trust nobody.
+     * @param statusReceiver    to receive the resulting checksums
+     */
+    public static void getChecksums(List<Pair<String, File>> filesToChecksum,
+            @PackageManager.FileChecksumKind int optional,
+            @PackageManager.FileChecksumKind int required,
+            @Nullable Certificate[] trustedInstallers,
+            @NonNull IntentSender statusReceiver,
+            @NonNull Injector injector) {
+        List<Map<Integer, FileChecksum>> result = new ArrayList<>(filesToChecksum.size());
+        for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
+            final String split = filesToChecksum.get(i).first;
+            final File file = filesToChecksum.get(i).second;
+            Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+            result.add(checksums);
+
+            try {
+                getAvailableFileChecksums(split, file, optional | required, trustedInstallers,
+                        checksums);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Preferred checksum calculation error", e);
+            }
+        }
+
+        long startTime = SystemClock.uptimeMillis();
+        processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
+                startTime);
+    }
+
+    private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
+            List<Map<Integer, FileChecksum>> result,
+            @PackageManager.FileChecksumKind int required,
+            @NonNull IntentSender statusReceiver,
+            @NonNull Injector injector,
+            long startTime) {
+        final boolean timeout =
+                SystemClock.uptimeMillis() - startTime >= PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS;
+        List<FileChecksum> allChecksums = new ArrayList<>();
+        for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
+            final String split = filesToChecksum.get(i).first;
+            final File file = filesToChecksum.get(i).second;
+            Map<Integer, FileChecksum> checksums = result.get(i);
+
+            try {
+                if (!timeout || required != 0) {
+                    if (needToWait(file, required, checksums, injector)) {
+                        // Not ready, come back later.
+                        injector.getHandler().postDelayed(() -> {
+                            processRequiredChecksums(filesToChecksum, result, required,
+                                    statusReceiver, injector, startTime);
+                        }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
+                        return;
+                    }
+
+                    getRequiredFileChecksums(split, file, required, checksums);
+                }
+                allChecksums.addAll(checksums.values());
+            } catch (Throwable e) {
+                Slog.e(TAG, "Required checksum calculation error", e);
+            }
+        }
+
+        final Intent intent = new Intent();
+        intent.putExtra(EXTRA_CHECKSUMS,
+                allChecksums.toArray(new FileChecksum[allChecksums.size()]));
+
+        try {
+            statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
+        } catch (IntentSender.SendIntentException e) {
+            Slog.w(TAG, e);
+        }
+    }
+
+    /**
+     * Fetch readily available checksums - enforced by kernel or provided by Installer.
+     *
+     * @param split             split name, null for base
+     * @param file              to fetch checksums for
+     * @param kinds             mask to fetch checksums
+     * @param trustedInstallers array of certificate to trust, two specific cases:
+     *                          null - trust anybody,
+     *                          [] - trust nobody.
+     * @param checksums         resulting checksums
+     */
+    private static void getAvailableFileChecksums(String split, File file,
+            @PackageManager.FileChecksumKind int kinds,
+            @Nullable Certificate[] trustedInstallers,
+            Map<Integer, FileChecksum> checksums) {
+        final String filePath = file.getAbsolutePath();
+
+        // Always available: FSI or IncFs.
+        if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) {
+            // Hashes in fs-verity and IncFS are always verified.
+            FileChecksum checksum = extractHashFromFS(split, filePath);
+            if (checksum != null) {
+                checksums.put(checksum.getKind(), checksum);
+            }
+        }
+
+        // System enforced: v2/v3.
+        if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired(
+                PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) {
+            Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature(
+                    split, filePath, kinds);
+            if (v2v3checksums != null) {
+                checksums.putAll(v2v3checksums);
+            }
+        }
+
+        // TODO(b/160605420): Installer provided.
+    }
+
+    /**
+     * Whether the file is available for checksumming or we need to wait.
+     */
+    private static boolean needToWait(File file,
+            @PackageManager.FileChecksumKind int kinds,
+            Map<Integer, FileChecksum> checksums,
+            @NonNull Injector injector) throws IOException {
+        if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)
+                && !isRequired(WHOLE_MD5, kinds, checksums)
+                && !isRequired(WHOLE_SHA1, kinds, checksums)
+                && !isRequired(WHOLE_SHA256, kinds, checksums)
+                && !isRequired(WHOLE_SHA512, kinds, checksums)
+                && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums)
+                && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) {
+            return false;
+        }
+
+        final String filePath = file.getAbsolutePath();
+        if (!IncrementalManager.isIncrementalPath(filePath)) {
+            return false;
+        }
+
+        IncrementalManager manager = injector.getIncrementalManager();
+        if (manager == null) {
+            throw new IllegalStateException("IncrementalManager is missing.");
+        }
+        IncrementalStorage storage = manager.openStorage(filePath);
+        if (storage == null) {
+            throw new IllegalStateException(
+                    "IncrementalStorage is missing for a path on IncFs: " + filePath);
+        }
+
+        return !storage.isFileFullyLoaded(filePath);
+    }
+
+    /**
+     * Fetch or calculate checksums for the specific file.
+     *
+     * @param split     split name, null for base
+     * @param file      to fetch checksums for
+     * @param kinds     mask to forcefully calculate if not available
+     * @param checksums resulting checksums
+     */
+    private static void getRequiredFileChecksums(String split, File file,
+            @PackageManager.FileChecksumKind int kinds,
+            Map<Integer, FileChecksum> checksums) {
+        final String filePath = file.getAbsolutePath();
+
+        // Manually calculating required checksums if not readily available.
+        if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) {
+            try {
+                byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash(
+                        filePath, /*salt=*/null,
+                        new ByteBufferFactory() {
+                            @Override
+                            public ByteBuffer create(int capacity) {
+                                return ByteBuffer.allocate(capacity);
+                            }
+                        });
+                checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256,
+                        new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash));
+            } catch (IOException | NoSuchAlgorithmException | DigestException e) {
+                Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e);
+            }
+        }
+
+        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5);
+        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1);
+        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256);
+        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512);
+
+        calculatePartialChecksumsIfRequested(checksums, split, file, kinds);
+    }
+
+    private static boolean isRequired(@PackageManager.FileChecksumKind int kind,
+            @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) {
+        if ((kinds & kind) == 0) {
+            return false;
+        }
+        if (checksums.containsKey(kind)) {
+            return false;
+        }
+        return true;
+    }
+
+    private static FileChecksum extractHashFromFS(String split, String filePath) {
+        // verity first
+        {
+            byte[] hash = VerityUtils.getFsverityRootHash(filePath);
+            if (hash != null) {
+                return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+            }
+        }
+        // v4 next
+        try {
+            ApkSignatureSchemeV4Verifier.VerifiedSigner signer =
+                    ApkSignatureSchemeV4Verifier.extractCertificates(filePath);
+            byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+                    null);
+            if (hash != null) {
+                return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+            }
+        } catch (SignatureNotFoundException e) {
+            // Nothing
+        } catch (SecurityException e) {
+            Slog.e(TAG, "V4 signature error", e);
+        }
+        return null;
+    }
+
+    private static Map<Integer, FileChecksum> extractHashFromV2V3Signature(
+            String split, String filePath, int kinds) {
+        Map<Integer, byte[]> contentDigests = null;
+        try {
+            contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
+                    PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
+                    false).contentDigests;
+        } catch (PackageParser.PackageParserException e) {
+            if (!(e.getCause() instanceof SignatureNotFoundException)) {
+                Slog.e(TAG, "Signature verification error", e);
+            }
+        }
+
+        if (contentDigests == null) {
+            return null;
+        }
+
+        Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
+            byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null);
+            if (hash != null) {
+                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256,
+                        new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
+            }
+        }
+        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
+            byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null);
+            if (hash != null) {
+                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512,
+                        new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
+            }
+        }
+        return checksums;
+    }
+
+    private static String getMessageDigestAlgoForChecksumKind(int kind)
+            throws NoSuchAlgorithmException {
+        switch (kind) {
+            case WHOLE_MD5:
+                return ALGO_MD5;
+            case WHOLE_SHA1:
+                return ALGO_SHA1;
+            case WHOLE_SHA256:
+                return ALGO_SHA256;
+            case WHOLE_SHA512:
+                return ALGO_SHA512;
+            default:
+                throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind);
+        }
+    }
+
+    private static void calculateChecksumIfRequested(Map<Integer, FileChecksum> checksums,
+            String split, File file, int required, int kind) {
+        if ((required & kind) != 0 && !checksums.containsKey(kind)) {
+            final byte[] checksum = getFileChecksum(file, kind);
+            if (checksum != null) {
+                checksums.put(kind, new FileChecksum(split, kind, checksum));
+            }
+        }
+    }
+
+    private static byte[] getFileChecksum(File file, int kind) {
+        try (FileInputStream fis = new FileInputStream(file);
+             BufferedInputStream bis = new BufferedInputStream(fis)) {
+            byte[] dataBytes = new byte[512 * 1024];
+            int nread = 0;
+
+            final String algo = getMessageDigestAlgoForChecksumKind(kind);
+            MessageDigest md = MessageDigest.getInstance(algo);
+            while ((nread = bis.read(dataBytes)) != -1) {
+                md.update(dataBytes, 0, nread);
+            }
+
+            return md.digest();
+        } catch (IOException e) {
+            Slog.e(TAG, "Error reading " + file.getAbsolutePath() + " to compute hash.", e);
+            return null;
+        } catch (NoSuchAlgorithmException e) {
+            Slog.e(TAG, "Device does not support MessageDigest algorithm", e);
+            return null;
+        }
+    }
+
+    private static int[] getContentDigestAlgos(boolean needSignatureSha256,
+            boolean needSignatureSha512) {
+        if (needSignatureSha256 && needSignatureSha512) {
+            // Signature block present, but no digests???
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA256, CONTENT_DIGEST_CHUNKED_SHA512};
+        } else if (needSignatureSha256) {
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA256};
+        } else {
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA512};
+        }
+    }
+
+    private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) {
+        switch (contentDigestAlgo) {
+            case CONTENT_DIGEST_CHUNKED_SHA256:
+                return PARTIAL_MERKLE_ROOT_1M_SHA256;
+            case CONTENT_DIGEST_CHUNKED_SHA512:
+                return PARTIAL_MERKLE_ROOT_1M_SHA512;
+            default:
+                return -1;
+        }
+    }
+
+    private static void calculatePartialChecksumsIfRequested(Map<Integer, FileChecksum> checksums,
+            String split, File file, int required) {
+        boolean needSignatureSha256 =
+                (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
+                        PARTIAL_MERKLE_ROOT_1M_SHA256);
+        boolean needSignatureSha512 =
+                (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey(
+                        PARTIAL_MERKLE_ROOT_1M_SHA512);
+        if (!needSignatureSha256 && !needSignatureSha512) {
+            return;
+        }
+
+        try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
+            SignatureInfo signatureInfo = null;
+            try {
+                signatureInfo = ApkSignatureSchemeV3Verifier.findSignature(raf);
+            } catch (SignatureNotFoundException e) {
+                try {
+                    signatureInfo = ApkSignatureSchemeV2Verifier.findSignature(raf);
+                } catch (SignatureNotFoundException ee) {
+                }
+            }
+            if (signatureInfo == null) {
+                Slog.e(TAG, "V2/V3 signatures not found in " + file.getAbsolutePath());
+                return;
+            }
+
+            final int[] digestAlgos = getContentDigestAlgos(needSignatureSha256,
+                    needSignatureSha512);
+            byte[][] digests = ApkSigningBlockUtils.computeContentDigestsPer1MbChunk(digestAlgos,
+                    raf.getFD(), signatureInfo);
+            for (int i = 0, size = digestAlgos.length; i < size; ++i) {
+                int checksumKind = getChecksumKindForContentDigestAlgo(digestAlgos[i]);
+                if (checksumKind != -1) {
+                    checksums.put(checksumKind, new FileChecksum(split, checksumKind, digests[i]));
+                }
+            }
+        } catch (IOException | DigestException e) {
+            Slog.e(TAG, "Error computing hash.", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 3d7c978..f168ac7 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -560,9 +560,9 @@
         final boolean newIsForceQueryable =
                 mForceQueryable.contains(newPkgSetting.appId)
                         /* shared user that is already force queryable */
-                        || newPkg.isForceQueryable()
-                        || newPkgSetting.forceQueryableOverride
+                        || newPkgSetting.forceQueryableOverride /* adb override */
                         || (newPkgSetting.isSystem() && (mSystemAppsQueryable
+                        || newPkg.isForceQueryable()
                         || ArrayUtils.contains(mForceQueryableByDevicePackageNames,
                         newPkg.getPackageName())));
         if (newIsForceQueryable
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 6b1b6d4..4be509b 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -889,54 +889,55 @@
             return;
         }
 
-        if (systemActivities == null) {
-            // the system package is not disabled; we're parsing the system partition
-            if (isProtectedAction(intent)) {
-                if (mDeferProtectedFilters) {
-                    // We can't deal with these just yet. No component should ever obtain a
-                    // >0 priority for a protected actions, with ONE exception -- the setup
-                    // wizard. The setup wizard, however, cannot be known until we're able to
-                    // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do
-                    // until all intent filters have been processed. Chicken, meet egg.
-                    // Let the filter temporarily have a high priority and rectify the
-                    // priorities after all system packages have been scanned.
-                    if (mProtectedFilters == null) {
-                        mProtectedFilters = new ArrayList<>();
-                    }
-                    mProtectedFilters.add(Pair.create(activity, intent));
+        if (isProtectedAction(intent)) {
+            if (mDeferProtectedFilters) {
+                // We can't deal with these just yet. No component should ever obtain a
+                // >0 priority for a protected actions, with ONE exception -- the setup
+                // wizard. The setup wizard, however, cannot be known until we're able to
+                // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do
+                // until all intent filters have been processed. Chicken, meet egg.
+                // Let the filter temporarily have a high priority and rectify the
+                // priorities after all system packages have been scanned.
+                if (mProtectedFilters == null) {
+                    mProtectedFilters = new ArrayList<>();
+                }
+                mProtectedFilters.add(Pair.create(activity, intent));
+                if (DEBUG_FILTERS) {
+                    Slog.i(TAG, "Protected action; save for later;"
+                            + " package: " + packageName
+                            + " activity: " + className
+                            + " origPrio: " + intent.getPriority());
+                }
+            } else {
+                if (DEBUG_FILTERS && setupWizardPackage == null) {
+                    Slog.i(TAG, "No setup wizard;"
+                            + " All protected intents capped to priority 0");
+                }
+                if (packageName.equals(setupWizardPackage)) {
                     if (DEBUG_FILTERS) {
-                        Slog.i(TAG, "Protected action; save for later;"
+                        Slog.i(TAG, "Found setup wizard;"
+                                + " allow priority " + intent.getPriority() + ";"
                                 + " package: " + packageName
                                 + " activity: " + className
-                                + " origPrio: " + intent.getPriority());
+                                + " priority: " + intent.getPriority());
                     }
-                    return;
-                } else {
-                    if (DEBUG_FILTERS && setupWizardPackage == null) {
-                        Slog.i(TAG, "No setup wizard;"
-                                + " All protected intents capped to priority 0");
-                    }
-                    if (packageName.equals(setupWizardPackage)) {
-                        if (DEBUG_FILTERS) {
-                            Slog.i(TAG, "Found setup wizard;"
-                                    + " allow priority " + intent.getPriority() + ";"
-                                    + " package: " + packageName
-                                    + " activity: " + className
-                                    + " priority: " + intent.getPriority());
-                        }
-                        // setup wizard gets whatever it wants
-                        return;
-                    }
-                    if (DEBUG_FILTERS) {
-                        Slog.i(TAG, "Protected action; cap priority to 0;"
-                                + " package: " + packageName
-                                + " activity: " + className
-                                + " origPrio: " + intent.getPriority());
-                    }
-                    intent.setPriority(0);
+                    // setup wizard gets whatever it wants
                     return;
                 }
+                if (DEBUG_FILTERS) {
+                    Slog.i(TAG, "Protected action; cap priority to 0;"
+                            + " package: " + packageName
+                            + " activity: " + className
+                            + " origPrio: " + intent.getPriority());
+                }
+                intent.setPriority(0);
             }
+            return;
+        }
+
+        if (systemActivities == null) {
+            // the system package is not disabled; we're parsing the system partition
+
             // privileged apps on the system image get whatever priority they request
             return;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 4b246c3..155af82 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -656,7 +656,7 @@
                     throw new IllegalArgumentException("Invalid install mode: " + params.mode);
             }
 
-            // If caller requested explicit location, sanity check it, otherwise
+            // If caller requested explicit location, validity check it, otherwise
             // resolve the best internal or adopted location.
             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
                 if (!PackageHelper.fitsOnInternal(mContext, params)) {
@@ -688,7 +688,7 @@
         final int sessionId;
         final PackageInstallerSession session;
         synchronized (mSessions) {
-            // Sanity check that installer isn't going crazy
+            // Check that the installer does not have too many active sessions.
             final int activeCount = getSessionCount(mSessions, callingUid);
             if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
                     == PackageManager.PERMISSION_GRANTED) {
@@ -743,9 +743,8 @@
             mStagingManager.createSession(session);
         }
 
-        if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
-            mCallbacks.notifySessionCreated(session.sessionId, session.userId);
-        }
+        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
+
         writeSessionsAsync();
         return sessionId;
     }
@@ -1355,25 +1354,18 @@
 
     class InternalCallback {
         public void onSessionBadgingChanged(PackageInstallerSession session) {
-            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
-                mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
-            }
-
+            mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
             writeSessionsAsync();
         }
 
         public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
-            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
-                mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId,
-                        active);
-            }
+            mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId,
+                    active);
         }
 
         public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
-            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
-                mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId,
-                        progress);
-            }
+            mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId,
+                    progress);
         }
 
         public void onStagedSessionChanged(PackageInstallerSession session) {
@@ -1389,17 +1381,13 @@
         }
 
         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
-            if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
-                mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
-            }
+            mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
 
             mInstallHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    if (session.isStaged()) {
-                        if (!success) {
-                            mStagingManager.abortSession(session);
-                        }
+                    if (session.isStaged() && !success) {
+                        mStagingManager.abortSession(session);
                     }
                     synchronized (mSessions) {
                         if (!session.isStaged() || !success) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ed62362..ca12532 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -249,6 +249,9 @@
     private final PackageManagerService mPm;
     private final Handler mHandler;
     private final PackageSessionProvider mSessionProvider;
+    /**
+     * Note all calls must be done outside {@link #mLock} to prevent lock inversion.
+     */
     private final StagingManager mStagingManager;
 
     final int sessionId;
@@ -389,6 +392,20 @@
     private String mStagedSessionErrorMessage;
 
     /**
+     * The callback to run when pre-reboot verification has ended. Used by {@link #abandonStaged()}
+     * to delay session clean-up until it is safe to do so.
+     */
+    @GuardedBy("mLock")
+    @Nullable
+    private Runnable mPendingAbandonCallback;
+    /**
+     * {@code true} if pre-reboot verification is ongoing which means it is not safe for
+     * {@link #abandon()} to clean up staging directories.
+     */
+    @GuardedBy("mLock")
+    private boolean mInPreRebootVerification;
+
+    /**
      * Path to the validated base APK for this session, which may point at an
      * APK inside the session (when the session defines the base), or it may
      * point at the existing base APK (when adding splits to an existing app).
@@ -978,7 +995,7 @@
 
     private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
             ParcelFileDescriptor incomingFd) throws IOException {
-        // Quick sanity check of state, and allocate a pipe for ourselves. We
+        // Quick validity check of state, and allocate a pipe for ourselves. We
         // then do heavy disk allocation outside the lock, but this open pipe
         // will block any attempted install transitions.
         final RevocableFileDescriptor fd;
@@ -1454,26 +1471,54 @@
         // TODO(patb): since the work done here for a parent session in a multi-package install is
         //             mostly superficial, consider splitting this method for the parent and
         //             single / child sessions.
-        synchronized (mLock) {
-            if (mCommitted) {
-                return true;
+        try {
+            synchronized (mLock) {
+                if (mCommitted) {
+                    return true;
+                }
+                // Read transfers from the original owner stay open, but as the session's data
+                // cannot be modified anymore, there is no leak of information. For staged sessions,
+                // further validation is performed by the staging manager.
+                if (!params.isMultiPackage) {
+                    if (!prepareDataLoaderLocked()) {
+                        return false;
+                    }
+
+                    if (isApexInstallation()) {
+                        validateApexInstallLocked();
+                    } else {
+                        validateApkInstallLocked();
+                    }
+                }
             }
 
-            if (!streamAndValidateLocked()) {
-                return false;
+            if (params.isStaged) {
+                mStagingManager.checkNonOverlappingWithStagedSessions(this);
             }
 
-            // Client staging is fully done at this point
-            mClientProgress = 1f;
-            computeProgressLocked(true);
+            synchronized (mLock) {
+                if (mDestroyed) {
+                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                            "Session destroyed");
+                }
+                // Client staging is fully done at this point
+                mClientProgress = 1f;
+                computeProgressLocked(true);
 
-            // This ongoing commit should keep session active, even though client
-            // will probably close their end.
-            mActiveCount.incrementAndGet();
+                // This ongoing commit should keep session active, even though client
+                // will probably close their end.
+                mActiveCount.incrementAndGet();
 
-            mCommitted = true;
+                mCommitted = true;
+            }
+            return true;
+        } catch (PackageManagerException e) {
+            throw onSessionValidationFailure(e);
+        } catch (Throwable e) {
+            // Convert all exceptions into package manager exceptions as only those are handled
+            // in the code above.
+            throw onSessionValidationFailure(new PackageManagerException(e));
         }
-        return true;
     }
 
     @GuardedBy("mLock")
@@ -1511,44 +1556,6 @@
         }
     }
 
-    /**
-     * Prepare DataLoader and stream content for DataLoader sessions.
-     * Validate the contents of all session.
-     *
-     * @return false if the data loader could not be prepared.
-     * @throws PackageManagerException when an unrecoverable exception is encountered
-     */
-    @GuardedBy("mLock")
-    private boolean streamAndValidateLocked() throws PackageManagerException {
-        try {
-            // Read transfers from the original owner stay open, but as the session's data cannot
-            // be modified anymore, there is no leak of information. For staged sessions, further
-            // validation is performed by the staging manager.
-            if (!params.isMultiPackage) {
-                if (!prepareDataLoaderLocked()) {
-                    return false;
-                }
-
-                if (isApexInstallation()) {
-                    validateApexInstallLocked();
-                } else {
-                    validateApkInstallLocked();
-                }
-            }
-
-            if (params.isStaged) {
-                mStagingManager.checkNonOverlappingWithStagedSessions(this);
-            }
-            return true;
-        } catch (PackageManagerException e) {
-            throw onSessionValidationFailure(e);
-        } catch (Throwable e) {
-            // Convert all exceptions into package manager exceptions as only those are handled
-            // in the code above.
-            throw onSessionValidationFailure(new PackageManagerException(e));
-        }
-    }
-
     private PackageManagerException onSessionValidationFailure(PackageManagerException e) {
         onSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
         return e;
@@ -1561,19 +1568,20 @@
         dispatchSessionFinished(error, detailMessage, null);
     }
 
-    private void onSessionVerificationFailure(int error, String detailMessage) {
-        Slog.e(TAG, "Failed to verify session " + sessionId + " [" + detailMessage + "]");
+    private void onSessionVerificationFailure(int error, String msg) {
+        final String msgWithErrorCode = PackageManager.installStatusToString(error, msg);
+        Slog.e(TAG, "Failed to verify session " + sessionId + " [" + msgWithErrorCode + "]");
         // Session is sealed and committed but could not be verified, we need to destroy it.
         destroyInternal();
         if (isStaged()) {
             setStagedSessionFailed(
-                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, detailMessage);
+                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
             // TODO(b/136257624): Remove this once all verification logic has been transferred out
             //  of StagingManager.
-            mStagingManager.notifyVerificationComplete(sessionId);
+            mStagingManager.notifyVerificationComplete(this);
         } else {
             // Dispatch message to remove session from PackageInstallerService.
-            dispatchSessionFinished(error, detailMessage, null);
+            dispatchSessionFinished(error, msg, null);
         }
     }
 
@@ -1836,21 +1844,6 @@
             throws PackageManagerException {
         assertNotLocked("makeSessionActive");
 
-        synchronized (mLock) {
-            if (mRelinquished) {
-                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                        "Session relinquished");
-            }
-            if (mDestroyed) {
-                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                        "Session destroyed");
-            }
-            if (!mSealed) {
-                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                        "Session not sealed");
-            }
-        }
-
         // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
         if (!params.isMultiPackage && needToAskForPermissions()) {
             // User needs to confirm installation;
@@ -1880,6 +1873,19 @@
     @GuardedBy("mLock")
     private PackageManagerService.VerificationParams makeVerificationParamsLocked()
             throws PackageManagerException {
+        if (mRelinquished) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Session relinquished");
+        }
+        if (mDestroyed) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Session destroyed");
+        }
+        if (!mSealed) {
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Session not sealed");
+        }
+
         // TODO(b/136257624): Some logic in this if block probably belongs in
         //  makeInstallParams().
         if (!params.isMultiPackage && !isApexInstallation()) {
@@ -2786,14 +2792,9 @@
     }
 
     private void abandonStaged() {
+        final Runnable r;
         synchronized (mLock) {
-            if (mDestroyed) {
-                // If a user abandons staged session in an unsafe state, then system will try to
-                // abandon the destroyed staged session when it is safe on behalf of the user.
-                assertCallerIsOwnerOrRootOrSystemLocked();
-            } else {
-                assertCallerIsOwnerOrRootLocked();
-            }
+            assertCallerIsOwnerOrRootLocked();
             if (isStagedAndInTerminalState()) {
                 // We keep the session in the database if it's in a finalized state. It will be
                 // removed by PackageInstallerService when the last update time is old enough.
@@ -2802,17 +2803,25 @@
                 return;
             }
             mDestroyed = true;
-            if (mCommitted) {
-                if (!mStagingManager.abortCommittedSessionLocked(this)) {
-                    // Do not clean up the staged session from system. It is not safe yet.
-                    mCallback.onStagedSessionChanged(this);
-                    return;
+            boolean isCommitted = mCommitted;
+            List<PackageInstallerSession> childSessions = getChildSessionsLocked();
+            r = () -> {
+                assertNotLocked("abandonStaged");
+                if (isCommitted) {
+                    mStagingManager.abortCommittedSession(this);
                 }
+                cleanStageDir(childSessions);
+                destroyInternal();
+                dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
+            };
+            if (mInPreRebootVerification) {
+                // Pre-reboot verification is ongoing. It is not safe to clean up the session yet.
+                mPendingAbandonCallback = r;
+                mCallback.onStagedSessionChanged(this);
+                return;
             }
-            cleanStageDir(getChildSessionsLocked());
-            destroyInternal();
         }
-        dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
+        r.run();
     }
 
     @Override
@@ -2829,6 +2838,50 @@
         }
     }
 
+    /**
+     * Notified by the staging manager that pre-reboot verification is about to start. The return
+     * value should be checked to decide whether it is OK to start pre-reboot verification. In
+     * the case of a destroyed session, {@code false} is returned and there is no need to start
+     * pre-reboot verification.
+     */
+    boolean notifyStagedStartPreRebootVerification() {
+        synchronized (mLock) {
+            if (mInPreRebootVerification) {
+                throw new IllegalStateException("Pre-reboot verification has started");
+            }
+            if (mDestroyed) {
+                return false;
+            }
+            mInPreRebootVerification = true;
+            return true;
+        }
+    }
+
+    private void dispatchPendingAbandonCallback() {
+        final Runnable callback;
+        synchronized (mLock) {
+            callback = mPendingAbandonCallback;
+            mPendingAbandonCallback = null;
+        }
+        if (callback != null) {
+            callback.run();
+        }
+    }
+
+    /**
+     * Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
+     * clean up the session if {@link #abandon()} has been called previously.
+     */
+    void notifyStagedEndPreRebootVerification() {
+        synchronized (mLock) {
+            if (!mInPreRebootVerification) {
+                throw new IllegalStateException("Pre-reboot verification not started");
+            }
+            mInPreRebootVerification = false;
+        }
+        dispatchPendingAbandonCallback();
+    }
+
     @Override
     public boolean isMultiPackage() {
         return params.isMultiPackage;
@@ -3306,8 +3359,7 @@
         // Send broadcast to default launcher only if it's a new install
         // TODO(b/144270665): Secure the usage of this broadcast.
         final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
-        if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()
-                && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
+        if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) {
             mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId);
         }
 
@@ -3744,7 +3796,7 @@
         out.endTag(null, TAG_SESSION);
     }
 
-    // Sanity check to be performed when the session is restored from an external file. Only one
+    // Validity check to be performed when the session is restored from an external file. Only one
     // of the session states should be true, or none of them.
     private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
                                                      boolean isFailed) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 31aa52b..7945d84 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -254,6 +254,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.ParcelableException;
 import android.os.PatternMatcher;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -394,6 +395,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -404,7 +406,10 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -926,6 +931,7 @@
         private final Object mLock;
         private final Installer mInstaller;
         private final Object mInstallLock;
+        private final Handler mBackgroundHandler;
         private final Executor mBackgroundExecutor;
 
         // ----- producers -----
@@ -948,7 +954,7 @@
 
         Injector(Context context, Object lock, Installer installer,
                 Object installLock, PackageAbiHelper abiHelper,
-                Executor backgroundExecutor,
+                Handler backgroundHandler,
                 Producer<ComponentResolver> componentResolverProducer,
                 Producer<PermissionManagerServiceInternal> permissionManagerProducer,
                 Producer<UserManagerService> userManagerProducer,
@@ -970,7 +976,8 @@
             mInstaller = installer;
             mAbiHelper = abiHelper;
             mInstallLock = installLock;
-            mBackgroundExecutor = backgroundExecutor;
+            mBackgroundHandler = backgroundHandler;
+            mBackgroundExecutor = new HandlerExecutor(backgroundHandler);
             mComponentResolverProducer = new Singleton<>(componentResolverProducer);
             mPermissionManagerProducer = new Singleton<>(permissionManagerProducer);
             mUserManagerProducer = new Singleton<>(userManagerProducer);
@@ -1085,6 +1092,10 @@
             return mPlatformCompatProducer.get(this, mPackageManager);
         }
 
+        public Handler getBackgroundHandler() {
+            return mBackgroundHandler;
+        }
+
         public Executor getBackgroundExecutor() {
             return mBackgroundExecutor;
         }
@@ -2400,9 +2411,12 @@
         final int callingUserId = UserHandle.getUserId(callingUid);
 
         for (String packageName : packages) {
-            PackageSetting setting = mSettings.mPackages.get(packageName);
-            if (setting != null
-                    && !shouldFilterApplicationLocked(setting, callingUid, callingUserId)) {
+            final boolean filterApp;
+            synchronized (mLock) {
+                final PackageSetting ps = mSettings.getPackageLPr(packageName);
+                filterApp = shouldFilterApplicationLocked(ps, callingUid, callingUserId);
+            }
+            if (!filterApp) {
                 notifyInstallObserver(packageName);
             }
         }
@@ -2443,6 +2457,68 @@
         mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
     }
 
+    @Override
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @PackageManager.FileChecksumKind int optional,
+            @PackageManager.FileChecksumKind int required, @Nullable List trustedInstallers,
+            @NonNull IntentSender statusReceiver, int userId) {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(statusReceiver);
+
+        final ApplicationInfo applicationInfo = getApplicationInfoInternal(packageName, 0,
+                Binder.getCallingUid(), userId);
+        if (applicationInfo == null) {
+            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
+        }
+
+        List<Pair<String, File>> filesToChecksum = new ArrayList<>();
+
+        // Adding base split.
+        filesToChecksum.add(Pair.create(null, new File(applicationInfo.sourceDir)));
+
+        // Adding other splits.
+        if (includeSplits && applicationInfo.splitNames != null) {
+            for (int i = 0, size = applicationInfo.splitNames.length; i < size; ++i) {
+                filesToChecksum.add(Pair.create(applicationInfo.splitNames[i],
+                        new File(applicationInfo.splitSourceDirs[i])));
+            }
+        }
+
+        for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
+            final File file = filesToChecksum.get(i).second;
+            if (!file.exists()) {
+                throw new IllegalStateException("File not found: " + file.getPath());
+            }
+        }
+
+        final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates(
+                trustedInstallers) : null;
+
+        mInjector.getBackgroundExecutor().execute(() -> {
+            ApkChecksums.Injector injector = new ApkChecksums.Injector(
+                    () -> mContext,
+                    () -> mInjector.getBackgroundHandler(),
+                    () -> mContext.getSystemService(IncrementalManager.class));
+            ApkChecksums.getChecksums(filesToChecksum, optional, required, trustedCerts,
+                    statusReceiver, injector);
+        });
+    }
+
+    private static @NonNull Certificate[] decodeCertificates(@NonNull List certs) {
+        try {
+            final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            final Certificate[] result = new Certificate[certs.size()];
+            for (int i = 0, size = certs.size(); i < size; ++i) {
+                final InputStream is = new ByteArrayInputStream((byte[]) certs.get(i));
+                final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+                result[i] = cert;
+            }
+            return result;
+        } catch (CertificateException e) {
+            throw ExceptionUtils.propagate(e);
+        }
+    }
+
     /**
      * Gets the type of the external storage a package is installed on.
      * @param packageVolume The storage volume of the package.
@@ -2597,7 +2673,7 @@
 
         Injector injector = new Injector(
                 context, lock, installer, installLock, new PackageAbiHelperImpl(),
-                new HandlerExecutor(backgroundHandler),
+                backgroundHandler,
                 (i, pm) ->
                         new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
                 (i, pm) ->
@@ -3268,6 +3344,7 @@
                 // Remove disable package settings for updated system apps that were
                 // removed via an OTA. If the update is no longer present, remove the
                 // app completely. Otherwise, revoke their system privileges.
+                final int[] userIds = mUserManager.getUserIds();
                 for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
                     final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
                     final AndroidPackage pkg = mPackages.get(packageName);
@@ -3310,7 +3387,7 @@
                     // partition], completely remove the package data.
                     final PackageSetting ps = mSettings.mPackages.get(packageName);
                     if (ps != null && mPackages.get(packageName) == null) {
-                        removePackageDataLIF(ps, null, null, 0, false);
+                        removePackageDataLIF(ps, userIds, null, 0, false);
 
                     }
                     logCriticalInfo(Log.WARN, msg);
@@ -3777,8 +3854,9 @@
                         // If we don't, installing the system package fails during scan
                         enableSystemPackageLPw(stubPkg);
                     }
-                    installPackageFromSystemLIF(stubPkg.getCodePath(), null /*allUserHandles*/,
-                            null /*origUserHandles*/, true /*writeSettings*/);
+                    installPackageFromSystemLIF(stubPkg.getCodePath(),
+                            mUserManager.getUserIds() /*allUserHandles*/, null /*origUserHandles*/,
+                            true /*writeSettings*/);
                 } catch (PackageManagerException pme) {
                     // Serious WTF; we have to be able to install the stub
                     Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
@@ -8964,10 +9042,10 @@
         if (providerInfo == null) {
             return null;
         }
-        if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
-            return null;
-        }
         synchronized (mLock) {
+            if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
+                return null;
+            }
             final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
             final ComponentName component =
                     new ComponentName(providerInfo.packageName, providerInfo.name);
@@ -9054,9 +9132,11 @@
             String targetPackage, int flags) {
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
-        final PackageSetting ps = mSettings.mPackages.get(targetPackage);
-        if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
-            return ParceledListSlice.emptyList();
+        synchronized (mLock) {
+            final PackageSetting ps = mSettings.getPackageLPr(targetPackage);
+            if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
+                return ParceledListSlice.emptyList();
+            }
         }
         return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags,
                 callingUserId));
@@ -9504,8 +9584,8 @@
                 try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage(
                         parsedPackage.getPackageName(),
                         "scanPackageInternalLI")) {
-                    deletePackageLIF(parsedPackage.getPackageName(), null, true, null, 0, null,
-                            false, null);
+                    deletePackageLIF(parsedPackage.getPackageName(), null, true,
+                            mUserManager.getUserIds(), 0, null, false, null);
                 }
                 pkgSetting = null;
             } else if (newPkgVersionGreater) {
@@ -11587,7 +11667,7 @@
             configurePackageComponents(parsedPackage);
         }
 
-        final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting);
+        final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride);
         final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
 
         if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
@@ -13793,7 +13873,7 @@
         final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId);
         final long callingId = Binder.clearCallingIdentity();
         try {
-            final String activeLauncherPackageName = getActiveLauncherPackageName(userId);
+            final String activeLauncherPackageName = mPermissionManager.getDefaultHome(userId);
             final String dialerPackageName = mPermissionManager.getDefaultDialer(userId);
             for (int i = 0; i < packageNames.length; i++) {
                 canSuspend[i] = false;
@@ -13869,18 +13949,6 @@
         return canSuspend;
     }
 
-    private String getActiveLauncherPackageName(int userId) {
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(Intent.CATEGORY_HOME);
-        ResolveInfo resolveInfo = resolveIntent(
-                intent,
-                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                PackageManager.MATCH_DEFAULT_ONLY,
-                userId);
-
-        return resolveInfo == null ? null : resolveInfo.activityInfo.packageName;
-    }
-
     @Override
     public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
         mContext.enforceCallingOrSelfPermission(
@@ -14572,7 +14640,7 @@
         final PackageSetting ps;
         int appId = -1;
         long ceDataInode = -1;
-        synchronized (mSettings) {
+        synchronized (mLock) {
             ps = mSettings.getPackageLPr(packageName);
             if (ps != null) {
                 appId = ps.appId;
@@ -16322,9 +16390,11 @@
      */
     private static class CommitRequest {
         final Map<String, ReconciledPackage> reconciledPackages;
+        @NonNull
         final int[] mAllUsers;
 
-        private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, int[] allUsers) {
+        private CommitRequest(Map<String, ReconciledPackage> reconciledPackages,
+                @NonNull int[] allUsers) {
             this.reconciledPackages = reconciledPackages;
             this.mAllUsers = allUsers;
         }
@@ -17620,7 +17690,7 @@
                 }
                 boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
                         && pkgSetting.getPkgState().isUpdatedSystemApp();
-                final String abiOverride = deriveAbiOverride(args.abiOverride, pkgSetting);
+                final String abiOverride = deriveAbiOverride(args.abiOverride);
                 AndroidPackage oldPackage = mPackages.get(pkgName);
                 boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
                 final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
@@ -18836,7 +18906,7 @@
      * make sure this flag is set for partially installed apps. If not its meaningless to
      * delete a partially installed application.
      */
-    private void removePackageDataLIF(final PackageSetting deletedPs, int[] allUserHandles,
+    private void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
             PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
         String packageName = deletedPs.name;
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
@@ -18924,7 +18994,7 @@
             }
             // make sure to preserve per-user disabled state if this removal was just
             // a downgrade of a system app to the factory package
-            if (allUserHandles != null && outInfo != null && outInfo.origUsers != null) {
+            if (outInfo != null && outInfo.origUsers != null) {
                 if (DEBUG_REMOVE) {
                     Slog.d(TAG, "Propagating install state across downgrade");
                 }
@@ -18977,11 +19047,10 @@
      * Tries to delete system package.
      */
     private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs,
-            int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
+            @NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
             boolean writeSettings)
             throws SystemDeleteException {
-        final boolean applyUserRestrictions =
-                (allUserHandles != null) && outInfo != null && (outInfo.origUsers != null);
+        final boolean applyUserRestrictions = outInfo != null && (outInfo.origUsers != null);
         final AndroidPackage deletedPkg = deletedPs.pkg;
         // Confirm if the system package has been updated
         // An updated system app can be deleted. This will also have to restore
@@ -19065,7 +19134,7 @@
      * Installs a package that's already on the system partition.
      */
     private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString,
-            @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
+            @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
             throws PackageManagerException {
         final File codePath = new File(codePathString);
         @ParseFlags int parseFlags =
@@ -19107,8 +19176,7 @@
             // and granting install permissions.
             mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
 
-            final boolean applyUserRestrictions
-                    = (allUserHandles != null) && (origUserHandles != null);
+            final boolean applyUserRestrictions = origUserHandles != null;
             if (applyUserRestrictions) {
                 boolean installedStateChanged = false;
                 if (DEBUG_REMOVE) {
@@ -19145,7 +19213,7 @@
     }
 
     private void deleteInstalledPackageLIF(PackageSetting ps,
-            boolean deleteCodeAndResources, int flags, int[] allUserHandles,
+            boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
             PackageRemovedInfo outInfo, boolean writeSettings) {
         synchronized (mLock) {
             if (outInfo != null) {
@@ -19265,7 +19333,7 @@
      * This method handles package deletion in general
      */
     private boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
-            boolean deleteCodeAndResources, int[] allUserHandles, int flags,
+            boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags,
             PackageRemovedInfo outInfo, boolean writeSettings,
             ParsedPackage replacingPackage) {
         final DeletePackageAction action;
@@ -19302,7 +19370,7 @@
     /** Deletes a package. Only throws when install of a disabled package fails. */
     private void executeDeletePackageLIF(DeletePackageAction action,
             String packageName, boolean deleteCodeAndResources,
-            int[] allUserHandles, boolean writeSettings,
+            @NonNull int[] allUserHandles, boolean writeSettings,
             ParsedPackage replacingPackage) throws SystemDeleteException {
         final PackageSetting ps = action.deletingPs;
         final PackageRemovedInfo outInfo = action.outInfo;
@@ -19452,6 +19520,9 @@
         }
 
         if (outInfo != null) {
+            if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+                outInfo.dataRemoved = true;
+            }
             outInfo.removedPackage = ps.name;
             outInfo.installerPackageName = ps.installSource.installerPackageName;
             outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
@@ -19487,9 +19558,11 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */, "clear application data");
 
-        final PackageSetting ps = mSettings.getPackageLPr(packageName);
-        final boolean filterApp =
-                (ps != null && shouldFilterApplicationLocked(ps, callingUid, userId));
+        final boolean filterApp;
+        synchronized (mLock) {
+            final PackageSetting ps = mSettings.getPackageLPr(packageName);
+            filterApp = shouldFilterApplicationLocked(ps, callingUid, userId);
+        }
         if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) {
             throw new SecurityException("Cannot clear data for a protected package: "
                     + packageName);
@@ -19769,11 +19842,13 @@
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                 != PackageManager.PERMISSION_GRANTED) {
-            if (getUidTargetSdkVersionLockedLPr(callingUid)
-                    < Build.VERSION_CODES.FROYO) {
-                Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
-                        + callingUid);
-                return;
+            synchronized (mLock) {
+                if (getUidTargetSdkVersionLockedLPr(callingUid)
+                        < Build.VERSION_CODES.FROYO) {
+                    Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
+                            + callingUid);
+                    return;
+                }
             }
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
@@ -19791,7 +19866,7 @@
             final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
             final ArrayList<PreferredActivity> existing = pir.findFilters(filter);
             if (removeExisting && existing != null) {
-                removeFiltersLocked(pir, filter, existing);
+                mSettings.removeFiltersLPw(pir, filter, existing);
             }
             pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
             scheduleWritePackageRestrictionsLocked(userId);
@@ -19892,7 +19967,7 @@
                     }
                 }
                 if (existing != null) {
-                    removeFiltersLocked(pir, filter, existing);
+                    mSettings.removeFiltersLPw(pir, filter, existing);
                 }
             }
         }
@@ -19900,22 +19975,6 @@
                 "Replacing preferred", false);
     }
 
-    private void removeFiltersLocked(@NonNull PreferredIntentResolver pir,
-            @NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) {
-        if (DEBUG_PREFERRED) {
-            Slog.i(TAG, existing.size() + " preferred matches for:");
-            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
-        }
-        for (int i = existing.size() - 1; i >= 0; --i) {
-            final PreferredActivity pa = existing.get(i);
-            if (DEBUG_PREFERRED) {
-                Slog.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":");
-                pa.dump(new LogPrinter(Log.INFO, TAG), "  ");
-            }
-            pir.removeFilter(pa);
-        }
-    }
-
     @Override
     public void clearPackagePreferredActivities(String packageName) {
         final int callingUid = Binder.getCallingUid();
@@ -19953,8 +20012,9 @@
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
     private void clearPackagePreferredActivities(String packageName, int userId) {
         final SparseBooleanArray changedUsers = new SparseBooleanArray();
-
-        clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
+        synchronized (mLock) {
+            clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
+        }
         if (changedUsers.size() > 0) {
             updateDefaultHomeNotLocked(changedUsers);
             postPreferredActivityChangedBroadcast(userId);
@@ -20076,7 +20136,9 @@
         // writer
         try {
             final SparseBooleanArray changedUsers = new SparseBooleanArray();
-            clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
+            synchronized (mLock) {
+                clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
+            }
             if (changedUsers.size() > 0) {
                 postPreferredActivityChangedBroadcast(userId);
             }
@@ -20544,6 +20606,9 @@
         if (cn != null) {
             return cn;
         }
+        // TODO: This should not happen since there should always be a default package set for
+        //  ROLE_HOME in RoleManager. Continue with a warning log for now.
+        Slog.w(TAG, "Default package for ROLE_HOME is not set in RoleManager");
 
         // Find the launcher with the highest priority and return that component if there are no
         // other home activity with the same priority.
@@ -20592,6 +20657,7 @@
         if (packageName == null) {
             return null;
         }
+
         int resolveInfosSize = resolveInfos.size();
         for (int i = 0; i < resolveInfosSize; i++) {
             ResolveInfo resolveInfo = resolveInfos.get(i);
@@ -20651,6 +20717,11 @@
             // PermissionController manages default home directly.
             return false;
         }
+
+        if (packageName == null) {
+            // Keep the default home package in RoleManager.
+            return false;
+        }
         mPermissionManager.setDefaultHome(packageName, userId, (successful) -> {
             if (successful) {
                 postPreferredActivityChangedBroadcast(userId);
@@ -21086,15 +21157,19 @@
         // Limit who can change which apps
         if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
             // Don't allow apps that don't have permission to modify other apps
-            if (!allowedByPermission
-                    || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
+            final boolean filterApp;
+            synchronized (mLock) {
+                filterApp = (!allowedByPermission
+                        || shouldFilterApplicationLocked(pkgSetting, callingUid, userId));
+            }
+            if (filterApp) {
                 throw new SecurityException(
                         "Attempt to change component state; "
-                        + "pid=" + Binder.getCallingPid()
-                        + ", uid=" + callingUid
-                        + (className == null
+                                + "pid=" + Binder.getCallingPid()
+                                + ", uid=" + callingUid
+                                + (className == null
                                 ? ", package=" + packageName
-                                : ", component=" + packageName + "/" + className));
+                                        : ", component=" + packageName + "/" + className));
             }
             // Don't allow changing protected packages.
             if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
@@ -21557,8 +21632,6 @@
                         .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
         co.onChange(true);
 
-        mAppsFilter.onSystemReady();
-
         // Disable any carrier apps. We do this very early in boot to prevent the apps from being
         // disabled after already being started.
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(
@@ -21707,6 +21780,9 @@
         mInstallerService.restoreAndApplyStagedSessionIfNeeded();
 
         mExistingPackages = null;
+
+        // We'll do this last as it builds its cache while holding mLock via callback.
+        mAppsFilter.onSystemReady();
     }
 
     public void waitForAppDataPrepared() {
@@ -22693,6 +22769,7 @@
             return;
         }
 
+        final int[] userIds = mUserManager.getUserIds();
         final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
         synchronized (mInstallLock) {
             synchronized (mLock) {
@@ -22706,7 +22783,7 @@
 
                     try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
                             "unloadPrivatePackagesInner")) {
-                        if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
+                        if (deletePackageLIF(ps.name, null, false, userIds, deleteFlags, outInfo,
                                 false, null)) {
                             unloaded.add(pkg);
                         } else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 491b4fc..5553cd0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -421,18 +421,13 @@
 
     /**
      * Derive the value of the {@code cpuAbiOverride} based on the provided
-     * value and an optional stored value from the package settings.
+     * value.
      */
-    public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
-        String cpuAbiOverride = null;
+    public static String deriveAbiOverride(String abiOverride) {
         if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
-            cpuAbiOverride = null;
-        } else if (abiOverride != null) {
-            cpuAbiOverride = abiOverride;
-        } else if (settings != null) {
-            cpuAbiOverride = settings.cpuAbiOverrideString;
+            return null;
         }
-        return cpuAbiOverride;
+        return abiOverride;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 3614cc0..2bbca79 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -67,7 +67,6 @@
                 }
             }
 
-            // Sanity check.
             if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) {
                 Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size());
             }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3ca23f9..5a8dd97 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3191,6 +3191,22 @@
         }
     }
 
+    void removeFiltersLPw(@NonNull PreferredIntentResolver pir,
+            @NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) {
+        if (PackageManagerService.DEBUG_PREFERRED) {
+            Slog.i(TAG, existing.size() + " preferred matches for:");
+            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
+        }
+        for (int i = existing.size() - 1; i >= 0; --i) {
+            final PreferredActivity pa = existing.get(i);
+            if (PackageManagerService.DEBUG_PREFERRED) {
+                Slog.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":");
+                pa.dump(new LogPrinter(Log.INFO, TAG), "  ");
+            }
+            pir.removeFilter(pa);
+        }
+    }
+
     private void applyDefaultPreferredActivityLPw(
             PackageManagerInternal pmInternal, IntentFilter tmpPa, ComponentName cn, int userId) {
         // The initial preferences only specify the target activity
@@ -3394,8 +3410,13 @@
                     Slog.w(TAG, "Malformed mimetype " + intent.getType() + " for " + cn);
                 }
             }
+            final PreferredIntentResolver pir = editPreferredActivitiesLPw(userId);
+            final List<PreferredActivity> existing = pir.findFilters(filter);
+            if (existing != null) {
+                removeFiltersLPw(pir, filter, existing);
+            }
             PreferredActivity pa = new PreferredActivity(filter, systemMatch, set, cn, true);
-            editPreferredActivitiesLPw(userId).addFilter(pa);
+            pir.addFilter(pa);
         } else if (haveNonSys == null) {
             StringBuilder sb = new StringBuilder();
             sb.append("No component ");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 0c4eaec3..462b215 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -60,7 +60,6 @@
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.apk.ApkSignatureVerifier;
 
@@ -1028,22 +1027,12 @@
 
     /**
      * <p>Abort committed staged session
-     *
-     * <p>This method must be called while holding {@link PackageInstallerSession#mLock}.
-     *
-     * <p>The method returns {@code false} to indicate it is not safe to clean up the session from
-     * system yet. When it is safe, the method returns {@code true}.
-     *
-     * <p> When it is safe to clean up, {@link StagingManager} will call
-     * {@link PackageInstallerSession#abandon()} on the session again.
-     *
-     * @return {@code true} if it is safe to cleanup the session resources, otherwise {@code false}.
      */
-    boolean abortCommittedSessionLocked(@NonNull PackageInstallerSession session) {
+    void abortCommittedSession(@NonNull PackageInstallerSession session) {
         int sessionId = session.sessionId;
-        if (session.isStagedSessionApplied()) {
-            Slog.w(TAG, "Cannot abort applied session : " + sessionId);
-            return false;
+        if (session.isStagedAndInTerminalState()) {
+            Slog.w(TAG, "Cannot abort session in final state: " + sessionId);
+            return;
         }
         if (!session.isDestroyed()) {
             throw new IllegalStateException("Committed session must be destroyed before aborting it"
@@ -1051,15 +1040,7 @@
         }
         if (getStagedSession(sessionId) == null) {
             Slog.w(TAG, "Session " + sessionId + " has been abandoned already");
-            return false;
-        }
-
-        // If pre-reboot verification is running, then return false. StagingManager will call
-        // abandon again when pre-reboot verification ends.
-        if (mPreRebootVerificationHandler.isVerificationRunning(sessionId)) {
-            Slog.w(TAG, "Session " + sessionId + " aborted before pre-reboot "
-                    + "verification completed.");
-            return false;
+            return;
         }
 
         // A session could be marked ready once its pre-reboot verification ends
@@ -1075,7 +1056,6 @@
         // Session was successfully aborted from apexd (if required) and pre-reboot verification
         // is also complete. It is now safe to clean up the session from system.
         abortSession(session);
-        return true;
     }
 
     /**
@@ -1264,8 +1244,8 @@
     // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
     //  verification logic is extracted out of StagingManager into PMS, we can remove
     //  this.
-    void notifyVerificationComplete(int sessionId) {
-        mPreRebootVerificationHandler.onPreRebootVerificationComplete(sessionId);
+    void notifyVerificationComplete(PackageInstallerSession session) {
+        mPreRebootVerificationHandler.onPreRebootVerificationComplete(session);
     }
 
     // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
@@ -1279,8 +1259,6 @@
         // Hold session ids before handler gets ready to do the verification.
         private IntArray mPendingSessionIds;
         private boolean mIsReady;
-        @GuardedBy("mVerificationRunning")
-        private final SparseBooleanArray mVerificationRunning = new SparseBooleanArray();
 
         PreRebootVerificationHandler(Looper looper) {
             super(looper);
@@ -1316,7 +1294,7 @@
             }
             if (session.isDestroyed() || session.isStagedSessionFailed()) {
                 // No point in running verification on a destroyed/failed session
-                onPreRebootVerificationComplete(sessionId);
+                onPreRebootVerificationComplete(session);
                 return;
             }
             switch (msg.what) {
@@ -1357,15 +1335,10 @@
             }
 
             PackageInstallerSession session = getStagedSession(sessionId);
-            synchronized (mVerificationRunning) {
-                // Do not start verification on a session that has been abandoned
-                if (session == null || session.isDestroyed()) {
-                    return;
-                }
+            if (session != null && session.notifyStagedStartPreRebootVerification()) {
                 Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
-                mVerificationRunning.put(sessionId, true);
+                obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
             }
-            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
         }
 
         private void onPreRebootVerificationFailure(PackageInstallerSession session,
@@ -1376,28 +1349,14 @@
                 // failed on next step and staging directory for session will be deleted.
             }
             session.setStagedSessionFailed(errorCode, errorMessage);
-            onPreRebootVerificationComplete(session.sessionId);
+            onPreRebootVerificationComplete(session);
         }
 
         // Things to do when pre-reboot verification completes for a particular sessionId
-        private void onPreRebootVerificationComplete(int sessionId) {
-            // Remove it from mVerificationRunning so that verification is considered complete
-            synchronized (mVerificationRunning) {
-                Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId);
-                mVerificationRunning.delete(sessionId);
-            }
-            // Check if the session was destroyed while pre-reboot verification was running. If so,
-            // abandon it again.
-            PackageInstallerSession session = getStagedSession(sessionId);
-            if (session != null && session.isDestroyed()) {
-                session.abandon();
-            }
-        }
-
-        private boolean isVerificationRunning(int sessionId) {
-            synchronized (mVerificationRunning) {
-                return mVerificationRunning.get(sessionId);
-            }
+        private void onPreRebootVerificationComplete(PackageInstallerSession session) {
+            int sessionId = session.sessionId;
+            Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId);
+            session.notifyStagedEndPreRebootVerification();
         }
 
         private void notifyPreRebootVerification_Start_Complete(int sessionId) {
@@ -1516,7 +1475,7 @@
             // or activate its apex, there won't be any files to work with as they will be cleaned
             // up by the system as part of abandonment. If session is abandoned before this point,
             // then the session is already destroyed and cannot be marked ready anymore.
-            onPreRebootVerificationComplete(session.sessionId);
+            onPreRebootVerificationComplete(session);
 
             // Proactively mark session as ready before calling apexd. Although this call order
             // looks counter-intuitive, this is the easiest way to ensure that session won't end up
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index dbe96e6..4858682 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -33,6 +33,9 @@
       "name": "CtsContentTestCases",
       "options": [
         {
+          "include-filter": "android.content.pm.cts.ChecksumsTest"
+        },
+        {
           "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
         },
         {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d137fd0..6ecaab6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -407,6 +407,10 @@
 
     @GuardedBy("mUsersLock")
     private int[] mUserIds;
+
+    @GuardedBy("mUsersLock")
+    private int[] mUserIdsIncludingPreCreated;
+
     @GuardedBy("mPackagesLock")
     private int mNextSerialNumber;
     private int mUserVersion = 0;
@@ -760,6 +764,8 @@
         return null;
     }
 
+    // TODO(b/157921703): replace by getAliveUsers() or remove (so callers
+    // explicitly call the 3-booleans version)
     public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
         return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */
                 true);
@@ -2494,16 +2500,35 @@
     }
 
     /**
-     * Returns an array of user ids. This array is cached here for quick access, so do not modify or
-     * cache it elsewhere.
+     * Returns an array of user ids.
+     *
+     * <p>This array is cached here for quick access, so do not modify or cache it elsewhere.
+     *
      * @return the array of user ids.
      */
-    public int[] getUserIds() {
+    public @NonNull int[] getUserIds() {
         synchronized (mUsersLock) {
             return mUserIds;
         }
     }
 
+    /**
+     * Returns an array of user ids, including pre-created users.
+     *
+     * <p>This method should only used for the specific cases that need to handle pre-created users;
+     * most callers should call {@link #getUserIds()} instead.
+     *
+     * <p>This array is cached here for quick access, so do not modify or
+     * cache it elsewhere.
+     *
+     * @return the array of user ids.
+     */
+    public @NonNull int[] getUserIdsIncludingPreCreated() {
+        synchronized (mUsersLock) {
+            return mUserIdsIncludingPreCreated;
+        }
+    }
+
     @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
     private void readUserListLP() {
         if (!mUserListFile.exists()) {
@@ -4359,23 +4384,43 @@
      */
     private void updateUserIds() {
         int num = 0;
+        int numIncludingPreCreated = 0;
         synchronized (mUsersLock) {
             final int userSize = mUsers.size();
             for (int i = 0; i < userSize; i++) {
-                UserInfo userInfo = mUsers.valueAt(i).info;
-                if (!userInfo.partial && !userInfo.preCreated) {
-                    num++;
+                final UserInfo userInfo = mUsers.valueAt(i).info;
+                if (!userInfo.partial) {
+                    numIncludingPreCreated++;
+                    if (!userInfo.preCreated) {
+                        num++;
+                    }
                 }
             }
+            if (DBG) {
+                Slog.d(LOG_TAG, "updateUserIds(): numberUsers= " + num
+                        + " includingPreCreated=" + numIncludingPreCreated);
+            }
             final int[] newUsers = new int[num];
+            final int[] newUsersIncludingPreCreated = new int[numIncludingPreCreated];
+
             int n = 0;
+            int nIncludingPreCreated = 0;
             for (int i = 0; i < userSize; i++) {
-                UserInfo userInfo = mUsers.valueAt(i).info;
-                if (!userInfo.partial && !userInfo.preCreated) {
-                    newUsers[n++] = mUsers.keyAt(i);
+                final UserInfo userInfo = mUsers.valueAt(i).info;
+                if (!userInfo.partial) {
+                    final int userId = mUsers.keyAt(i);
+                    newUsersIncludingPreCreated[nIncludingPreCreated++] = userId;
+                    if (!userInfo.preCreated) {
+                        newUsers[n++] = userId;
+                    }
                 }
             }
             mUserIds = newUsers;
+            mUserIdsIncludingPreCreated = newUsersIncludingPreCreated;
+            if (DBG) {
+                Slog.d(LOG_TAG, "updateUserIds(): userIds= " + Arrays.toString(mUserIds)
+                        + " includingPreCreated=" + Arrays.toString(mUserIdsIncludingPreCreated));
+            }
         }
     }
 
@@ -4839,6 +4884,8 @@
             synchronized (mUsersLock) {
                 pw.print("  Cached user IDs: ");
                 pw.println(Arrays.toString(mUserIds));
+                pw.print("  Cached user IDs (including pre-created): ");
+                pw.println(Arrays.toString(mUserIdsIncludingPreCreated));
             }
 
         } // synchronized (mPackagesLock)
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 75a88e2..f7721a4 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -414,17 +414,14 @@
             if (pkg == null
                     || !doesPackageSupportRuntimePermissions(pkg)
                     || ArrayUtils.isEmpty(pkg.requestedPermissions)
-                    || !pkg.applicationInfo.isPrivilegedApp()) {
+                    || !pm.isGranted(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                            pkg, UserHandle.of(userId))) {
                 continue;
             }
-            for (String permission : pkg.requestedPermissions) {
-                if (Manifest.permission.READ_PRIVILEGED_PHONE_STATE.equals(permission)) {
-                    grantRuntimePermissions(pm, pkg,
-                            Collections.singleton(Manifest.permission.READ_PHONE_STATE),
-                            true, // systemFixed
-                            userId);
-                }
-            }
+            grantRuntimePermissions(pm, pkg,
+                    Collections.singleton(Manifest.permission.READ_PHONE_STATE),
+                    true, // systemFixed
+                    userId);
         }
 
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f5dd918..ffdcc22 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -83,7 +83,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
-import android.content.pm.UserInfo;
 import android.content.pm.parsing.component.ParsedPermission;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
@@ -121,7 +120,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
-import android.util.TimingsTraceLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -3146,17 +3144,7 @@
      * @return user ids for created users and pre-created users
      */
     private int[] getAllUserIds() {
-        final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
-        t.traceBegin("getAllUserIds");
-        List<UserInfo> users = UserManagerService.getInstance().getUsers(
-                /*excludePartial=*/ true, /*excludeDying=*/ true, /*excludePreCreated=*/ false);
-        int size = users.size();
-        final int[] userIds = new int[size];
-        for (int i = 0; i < size; i++) {
-            userIds[i] = users.get(i).id;
-        }
-        t.traceEnd();
-        return userIds;
+        return UserManagerService.getInstance().getUserIdsIncludingPreCreated();
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index ae2b040..e1cd9e3 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -31,6 +31,7 @@
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -242,8 +243,9 @@
             public void onReceive(Context context, Intent intent) {
                 boolean hasSetupRun = true;
                 try {
-                    hasSetupRun = Settings.Secure.getInt(getContext().getContentResolver(),
-                            Settings.Secure.USER_SETUP_COMPLETE) != 0;
+                    final ContentResolver cr = getContext().getContentResolver();
+                    hasSetupRun = Settings.Secure.getIntForUser(cr,
+                            Settings.Secure.USER_SETUP_COMPLETE, cr.getUserId()) != 0;
                 } catch (Settings.SettingNotFoundException e) {
                     // Ignore error, assume setup has run
                 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 137c587..d01a30f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -69,6 +69,7 @@
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -148,6 +149,7 @@
 import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
@@ -1378,12 +1380,14 @@
     }
 
     private long getScreenshotChordLongPressDelay() {
+        long delayMs = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
+                ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
         if (mKeyguardDelegate.isShowing()) {
             // Double the time it takes to take a screenshot from the keyguard
-            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
-                    ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
+            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs);
         }
-        return ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout();
+        return delayMs;
     }
 
     private long getRingerToggleChordDelay() {
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 2b793c8..f204aa2 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -52,6 +52,9 @@
     /** The maximum size of signature file.  This is just to avoid potential abuse. */
     private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
 
+    /** SHA256 hash size. */
+    private static final int HASH_SIZE_BYTES = 32;
+
     private static final boolean DEBUG = false;
 
     /** Returns true if the given file looks like containing an fs-verity signature. */
@@ -90,8 +93,23 @@
         return (retval == 1);
     }
 
+    /** Returns hash of a root node for the fs-verity enabled file. */
+    public static byte[] getFsverityRootHash(@NonNull String filePath) {
+        byte[] result = new byte[HASH_SIZE_BYTES];
+        int retval = measureFsverityNative(filePath, result);
+        if (retval < 0) {
+            if (retval != -OsConstants.ENODATA) {
+                Slog.e(TAG, "Failed to measure fs-verity, errno " + -retval + ": " + filePath);
+            }
+            return null;
+        }
+        return result;
+    }
+
     private static native int enableFsverityNative(@NonNull String filePath,
             @NonNull byte[] pkcs7Signature);
+    private static native int measureFsverityNative(@NonNull String filePath,
+            @NonNull byte[] digest);
     private static native int statxForFsverityNative(@NonNull String filePath);
 
     /**
diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java
index 6f5afa2..d25ddf8 100644
--- a/services/core/java/com/android/server/slice/SliceFullAccessList.java
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -101,7 +101,7 @@
     public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
         // upgrade xml
         int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
-        final List<UserInfo> activeUsers = UserManager.get(mContext).getUsers(true);
+        final List<UserInfo> activeUsers = UserManager.get(mContext).getAliveUsers();
         for (UserInfo userInfo : activeUsers) {
             upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier());
         }
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5aedfc1..2a74b3d 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -28,6 +28,8 @@
 import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
 import android.app.slice.ISliceManager;
 import android.app.slice.SliceSpec;
 import android.app.usage.UsageStatsManagerInternal;
@@ -39,7 +41,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
@@ -61,6 +65,7 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -72,7 +77,10 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
 
 public class SliceManagerService extends ISliceManager.Stub {
 
@@ -80,13 +88,16 @@
     private final Object mLock = new Object();
 
     private final Context mContext;
+    private final PackageManagerInternal mPackageManagerInternal;
     private final AppOpsManager mAppOps;
     private final AssistUtils mAssistUtils;
 
     @GuardedBy("mLock")
     private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
     @GuardedBy("mLock")
-    private final SparseArray<String> mLastAssistantPackage = new SparseArray<>();
+    private final SparseArray<PackageMatchingCache> mAssistantLookup = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final SparseArray<PackageMatchingCache> mHomeLookup = new SparseArray<>();
     private final Handler mHandler;
 
     private final SlicePermissionManager mPermissions;
@@ -99,6 +110,8 @@
     @VisibleForTesting
     SliceManagerService(Context context, Looper looper) {
         mContext = context;
+        mPackageManagerInternal = Objects.requireNonNull(
+                LocalServices.getService(PackageManagerInternal.class));
         mAppOps = context.getSystemService(AppOpsManager.class);
         mAssistUtils = new AssistUtils(context);
         mHandler = new Handler(looper);
@@ -111,6 +124,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
+        mRoleObserver = new RoleObserver();
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
     }
 
@@ -160,7 +174,8 @@
         mHandler.post(() -> {
             if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
                 mAppUsageStats.reportEvent(slicePkg, user,
-                        isAssistant(pkg, user) ? SLICE_PINNED_PRIV : SLICE_PINNED);
+                        isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
+                                ? SLICE_PINNED_PRIV : SLICE_PINNED);
             }
         });
     }
@@ -425,19 +440,38 @@
     private boolean hasFullSliceAccess(String pkg, int userId) {
         long ident = Binder.clearCallingIdentity();
         try {
-            return isAssistant(pkg, userId) || isGrantedFullAccess(pkg, userId);
+            boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
+                    || isGrantedFullAccess(pkg, userId);
+            return ret;
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
     private boolean isAssistant(String pkg, int userId) {
-        if (pkg == null) return false;
-        if (!pkg.equals(mLastAssistantPackage.get(userId))) {
-            // Failed on cached value, try updating.
-            mLastAssistantPackage.put(userId, getAssistant(userId));
+        return getAssistantMatcher(userId).matches(pkg);
+    }
+
+    private boolean isDefaultHomeApp(String pkg, int userId) {
+        return getHomeMatcher(userId).matches(pkg);
+    }
+
+    private PackageMatchingCache getAssistantMatcher(int userId) {
+        PackageMatchingCache matcher = mAssistantLookup.get(userId);
+        if (matcher == null) {
+            matcher = new PackageMatchingCache(() -> getAssistant(userId));
+            mAssistantLookup.put(userId, matcher);
         }
-        return pkg.equals(mLastAssistantPackage.get(userId));
+        return matcher;
+    }
+
+    private PackageMatchingCache getHomeMatcher(int userId) {
+        PackageMatchingCache matcher = mHomeLookup.get(userId);
+        if (matcher == null) {
+            matcher = new PackageMatchingCache(() -> getDefaultHome(userId));
+            mHomeLookup.put(userId, matcher);
+        }
+        return matcher;
     }
 
     private String getAssistant(int userId) {
@@ -448,6 +482,111 @@
         return cn.getPackageName();
     }
 
+    /**
+     * A cached value of the default home app
+     */
+    private String mCachedDefaultHome = null;
+
+    // Based on getDefaultHome in ShortcutService.
+    // TODO: Unify if possible
+    @VisibleForTesting
+    protected String getDefaultHome(int userId) {
+
+        // Set VERIFY to true to run the cache in "shadow" mode for cache
+        // testing.  Do not commit set to true;
+        final boolean VERIFY = false;
+
+        if (mCachedDefaultHome != null) {
+            if (!VERIFY) {
+                return mCachedDefaultHome;
+            }
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+
+            // Default launcher from package manager.
+            final ComponentName defaultLauncher = mPackageManagerInternal
+                    .getHomeActivitiesAsUser(allHomeCandidates, userId);
+
+            ComponentName detected = defaultLauncher;
+
+            // Cache the default launcher.  It is not a problem if the
+            // launcher is null - eventually, the default launcher will be
+            // set to something non-null.
+            mCachedDefaultHome = ((detected != null) ? detected.getPackageName() : null);
+
+            if (detected == null) {
+                // If we reach here, that means it's the first check since the user was created,
+                // and there's already multiple launchers and there's no default set.
+                // Find the system one with the highest priority.
+                // (We need to check the priority too because of FallbackHome in Settings.)
+                // If there's no system launcher yet, then no one can access slices, until
+                // the user explicitly sets one.
+                final int size = allHomeCandidates.size();
+
+                int lastPriority = Integer.MIN_VALUE;
+                for (int i = 0; i < size; i++) {
+                    final ResolveInfo ri = allHomeCandidates.get(i);
+                    if (!ri.activityInfo.applicationInfo.isSystemApp()) {
+                        continue;
+                    }
+                    if (ri.priority < lastPriority) {
+                        continue;
+                    }
+                    detected = ri.activityInfo.getComponentName();
+                    lastPriority = ri.priority;
+                }
+            }
+            final String ret = ((detected != null) ? detected.getPackageName() : null);
+            if (VERIFY) {
+                if (mCachedDefaultHome != null && !mCachedDefaultHome.equals(ret)) {
+                    Slog.e(TAG, "getDefaultHome() cache failure, is " +
+                           mCachedDefaultHome + " should be " + ret);
+                }
+            }
+            return ret;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void invalidateCachedDefaultHome() {
+        mCachedDefaultHome = null;
+    }
+
+    /**
+     * Listen for changes in the roles, and invalidate the cached default
+     * home as necessary.
+     */
+    private RoleObserver mRoleObserver;
+
+    class RoleObserver implements OnRoleHoldersChangedListener {
+        private RoleManager mRm;
+        private final Executor mExecutor;
+
+        RoleObserver() {
+            mExecutor = mContext.getMainExecutor();
+            register();
+        }
+
+        public void register() {
+            mRm = mContext.getSystemService(RoleManager.class);
+            if (mRm != null) {
+                mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL);
+                invalidateCachedDefaultHome();
+            }
+        }
+
+        @Override
+        public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
+            if (RoleManager.ROLE_HOME.equals(roleName)) {
+                invalidateCachedDefaultHome();
+            }
+        }
+    }
+
     private boolean isGrantedFullAccess(String pkg, int userId) {
         return mPermissions.hasFullAccess(pkg, userId);
     }
@@ -496,6 +635,30 @@
         return mPermissions.getAllPackagesGranted(pkg);
     }
 
+    /**
+     * Holder that caches a package that has access to a slice.
+     */
+    static class PackageMatchingCache {
+
+        private final Supplier<String> mPkgSource;
+        private String mCurrentPkg;
+
+        public PackageMatchingCache(Supplier<String> pkgSource) {
+            mPkgSource = pkgSource;
+        }
+
+        public boolean matches(String pkgCandidate) {
+            if (pkgCandidate == null) return false;
+
+            if (Objects.equals(pkgCandidate, mCurrentPkg)) {
+                return true;
+            }
+            // Failed on cached value, try updating.
+            mCurrentPkg = mPkgSource.get();
+            return Objects.equals(pkgCandidate, mCurrentPkg);
+        }
+    }
+
     public static class Lifecycle extends SystemService {
         private SliceManagerService mService;
 
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
index 8e5ecee..ebe9733 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
@@ -35,7 +35,8 @@
  * HAL whenever they expire.
  */
 public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
-    private static final long TIMEOUT_MS = 1000;
+    // TODO(b/166328980): Reduce this to 1000 as soon as HAL is fixed.
+    private static final long TIMEOUT_MS = 10000;
     private static final String TAG = "SoundTriggerHw2Watchdog";
 
     private final @NonNull
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index c382e11..5a587cc 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -36,7 +36,6 @@
 import android.os.IBinder;
 import android.os.IHwBinder;
 import android.os.RemoteException;
-import android.os.ServiceSpecificException;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -293,7 +292,11 @@
             } catch (Exception e) {
                 // We must do this outside the lock, to avoid possible deadlocks with the remote
                 // process that provides the audio sessions, which may also be calling into us.
-                mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                try {
+                    mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                } catch (Exception ee) {
+                    Log.e(TAG, "Failed to release session.", ee);
+                }
                 throw e;
             }
         }
@@ -321,7 +324,11 @@
             } catch (Exception e) {
                 // We must do this outside the lock, to avoid possible deadlocks with the remote
                 // process that provides the audio sessions, which may also be calling into us.
-                mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                try {
+                    mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                } catch (Exception ee) {
+                    Log.e(TAG, "Failed to release session.", ee);
+                }
                 throw e;
             }
         }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index f74cd611..0314cf8 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3005,10 +3005,10 @@
                           Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, 1, userId);
                 int unlockDismissesKeyguard = Settings.Secure.getIntForUser(
                           mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, 0, userId);
+                          Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, 1, userId);
                 int unlockAttentionRequired = Settings.Secure.getIntForUser(
                           mContext.getContentResolver(),
-                          Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 1, userId);
+                          Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 0, userId);
                 int unlockAppEnabled = Settings.Secure.getIntForUser(
                           mContext.getContentResolver(),
                           Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1, userId);
diff --git a/services/core/java/com/android/server/telecom/OWNERS b/services/core/java/com/android/server/telecom/OWNERS
new file mode 100644
index 0000000..39be2c1
--- /dev/null
+++ b/services/core/java/com/android/server/telecom/OWNERS
@@ -0,0 +1,6 @@
+breadley@google.com
+hallliu@google.com
+tgunn@google.com
+xiaotonj@google.com
+shuoq@google.com
+rgreenwalt@google.com
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index aee3d8d..b68c54f 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -25,6 +25,7 @@
 import android.annotation.UserIdInt;
 import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
+import android.os.UserHandle;
 
 import java.util.Objects;
 
@@ -56,6 +57,12 @@
         return mUserId;
     }
 
+    /** Returns the handle of the user this configuration is associated with. */
+    @NonNull
+    public UserHandle getUserHandle() {
+        return UserHandle.of(mUserId);
+    }
+
     /** Returns true if the user allowed to modify time zone configuration. */
     public boolean isUserConfigAllowed() {
         return mUserConfigAllowed;
@@ -198,13 +205,13 @@
 
     @Override
     public String toString() {
-        return "TimeZoneDetectorConfiguration{"
+        return "ConfigurationInternal{"
                 + "mUserId=" + mUserId
-                + "mUserConfigAllowed=" + mUserConfigAllowed
-                + "mAutoDetectionSupported=" + mAutoDetectionSupported
-                + "mAutoDetectionEnabled=" + mAutoDetectionEnabled
-                + "mLocationEnabled=" + mLocationEnabled
-                + "mGeoDetectionEnabled=" + mGeoDetectionEnabled
+                + ", mUserConfigAllowed=" + mUserConfigAllowed
+                + ", mAutoDetectionSupported=" + mAutoDetectionSupported
+                + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
+                + ", mLocationEnabled=" + mLocationEnabled
+                + ", mGeoDetectionEnabled=" + mGeoDetectionEnabled
                 + '}';
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
new file mode 100644
index 0000000..91e172c
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.timezonedetector."
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index 3230ef1..a8d5c02 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -23,7 +23,7 @@
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
-/** Implemented the shell command interface for {@link TimeZoneDetectorService}. */
+/** Implements the shell command interface for {@link TimeZoneDetectorService}. */
 class TimeZoneDetectorShellCommand extends ShellCommand {
 
     private final TimeZoneDetectorService mInterface;
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 0c85387..89b108c 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -126,6 +126,8 @@
     private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
     private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000;
 
+    private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android";
+
     private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
     private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
     private final Receiver mReceiver = new Receiver();
@@ -379,7 +381,7 @@
     }
 
     private void updateTrustAll() {
-        List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
+        List<UserInfo> userInfos = mUserManager.getAliveUsers();
         for (UserInfo userInfo : userInfos) {
             updateTrust(userInfo.id, 0);
         }
@@ -485,7 +487,7 @@
 
         List<UserInfo> userInfos;
         if (userIdOrAll == UserHandle.USER_ALL) {
-            userInfos = mUserManager.getUsers(true /* excludeDying */);
+            userInfos = mUserManager.getAliveUsers();
         } else {
             userInfos = new ArrayList<>();
             userInfos.add(mUserManager.getUserInfo(userIdOrAll));
@@ -644,7 +646,7 @@
         }
         List<UserInfo> userInfos;
         if (userId == UserHandle.USER_ALL) {
-            userInfos = mUserManager.getUsers(true /* excludeDying */);
+            userInfos = mUserManager.getAliveUsers();
         } else {
             userInfos = new ArrayList<>();
             userInfos.add(mUserManager.getUserInfo(userId));
@@ -811,8 +813,8 @@
             TypedArray sa = res
                     .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
             cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
-            canUnlockProfile = sa.getBoolean(
-                    com.android.internal.R.styleable.TrustAgent_unlockProfile, false);
+            canUnlockProfile = attrs.getAttributeBooleanValue(
+                    PRIV_NAMESPACE, "unlockProfile", false);
             sa.recycle();
         } catch (PackageManager.NameNotFoundException e) {
             caughtException = e;
@@ -1171,7 +1173,7 @@
                 fout.println("disabled because the third-party apps can't run yet.");
                 return;
             }
-            final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
+            final List<UserInfo> userInfos = mUserManager.getAliveUsers();
             mHandler.runWithScissors(new Runnable() {
                 @Override
                 public void run() {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 0b0bb70..53d5146 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -115,7 +115,7 @@
     private static final String TAG = "UriGrantsManagerService";
     // Maximum number of persisted Uri grants a package is allowed
     private static final int MAX_PERSISTED_URI_GRANTS = 512;
-    private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true;
+    private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false;
 
     private final Object mLock = new Object();
     private final H mH;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 56261c4..6df46ed 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -133,6 +133,7 @@
 import static com.android.server.wm.ActivityRecordProto.NAME;
 import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS;
 import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS;
+import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ALLOWED;
 import static com.android.server.wm.ActivityRecordProto.PROC_ID;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
@@ -401,7 +402,6 @@
 
     final ActivityTaskManagerService mAtmService;
     final ActivityInfo info; // activity info provided by developer in AndroidManifest
-    // Non-null only for application tokens.
     // TODO: rename to mActivityToken
     final ActivityRecord.Token appToken;
     // Which user is this running for?
@@ -1587,7 +1587,8 @@
         hasBeenLaunched = false;
         mStackSupervisor = supervisor;
 
-        info.taskAffinity = getTaskAffinityWithUid(info.taskAffinity, info.applicationInfo.uid);
+        info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid,
+                launchMode);
         taskAffinity = info.taskAffinity;
         final String uid = Integer.toString(info.applicationInfo.uid);
         if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
@@ -1648,17 +1649,18 @@
     }
 
     /**
-     * Generate the task affinity with uid. For b/35954083, Limit task affinity to uid to avoid
-     * issues associated with sharing affinity across uids.
+     * Generate the task affinity with uid and activity launch mode. For b/35954083, Limit task
+     * affinity to uid to avoid issues associated with sharing affinity across uids.
      *
      * @param affinity The affinity of the activity.
      * @param uid The user-ID that has been assigned to this application.
-     * @return The task affinity with uid.
+     * @param launchMode The activity launch mode
+     * @return The task affinity
      */
-    static String getTaskAffinityWithUid(String affinity, int uid) {
+    static String computeTaskAffinity(String affinity, int uid, int launchMode) {
         final String uidStr = Integer.toString(uid);
         if (affinity != null && !affinity.startsWith(uidStr)) {
-            affinity = uidStr + ":" + affinity;
+            affinity = uidStr + (launchMode == LAUNCH_SINGLE_INSTANCE ? "-si:" : ":") + affinity;
         }
         return affinity;
     }
@@ -2123,11 +2125,6 @@
         return task != null ? task.getRootTaskId() : INVALID_TASK_ID;
     }
 
-    DisplayContent getDisplay() {
-        final Task stack = getRootTask();
-        return stack != null ? stack.getDisplay() : null;
-    }
-
     @Override
     @Nullable
     TaskDisplayArea getDisplayArea() {
@@ -2387,7 +2384,10 @@
 
             }
         }
-        return (canReceiveKeys() || isAlwaysFocusable()) && getDisplay() != null;
+        // Check isAttached() because the method may be called when removing this activity from
+        // display, and WindowContainer#compareTo will throw exception if it doesn't have a parent
+        // when updating focused window from DisplayContent#findFocusedWindow.
+        return (canReceiveKeys() || isAlwaysFocusable()) && isAttached();
     }
 
     /**
@@ -2664,7 +2664,7 @@
     }
 
     private void prepareActivityHideTransitionAnimation(int transit) {
-        final DisplayContent dc = getDisplay().mDisplayContent;
+        final DisplayContent dc = mDisplayContent;
         dc.prepareAppTransition(transit, false);
         setVisibility(false);
         dc.executeAppTransition();
@@ -2709,7 +2709,7 @@
             }
 
             if (ensureVisibility) {
-                getDisplay().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                mDisplayContent.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                         false /* preserveWindows */, true /* notifyClients */);
             }
         }
@@ -4653,7 +4653,7 @@
         }
 
         // Check if the activity is on a sleeping display, and if it can turn it ON.
-        if (getDisplay().isSleeping()) {
+        if (mDisplayContent.isSleeping()) {
             final boolean canTurnScreenOn = !mSetToSleep || canTurnScreenOn()
                     || canShowWhenLocked() || containsDismissKeyguardWindow();
             if (!canTurnScreenOn) {
@@ -4742,6 +4742,11 @@
             // returns. Just need to confirm this reasoning makes sense.
             final boolean deferHidingClient = canEnterPictureInPicture
                     && !isState(STARTED, STOPPING, STOPPED, PAUSED);
+            if (deferHidingClient && pictureInPictureArgs.isAutoEnterAllowed()) {
+                // Go ahead and just put the activity in pip if it supports auto-pip.
+                mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs);
+                return;
+            }
             setDeferHidingClient(deferHidingClient);
             setVisibility(false);
 
@@ -4932,12 +4937,8 @@
         }
         r.setSavedState(null /* savedState */);
 
-        final DisplayContent display = r.getDisplay();
-        if (display != null) {
-            display.handleActivitySizeCompatModeIfNeeded(r);
-        }
-
-        r.getDisplayContent().mUnknownAppVisibilityController.notifyAppResumedFinished(r);
+        r.mDisplayContent.handleActivitySizeCompatModeIfNeeded(r);
+        r.mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(r);
     }
 
     /**
@@ -5480,10 +5481,6 @@
     }
 
     void updateReportedVisibilityLocked() {
-        if (appToken == null) {
-            return;
-        }
-
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this);
         final int count = mChildren.size();
 
@@ -6330,8 +6327,7 @@
     }
 
     private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
-        final IBinder binder =
-                (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null;
+        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
         setOrientation(requestedOrientation, binder, this);
 
         // Push the new configuration to the requested app in case where it's not pushed, e.g. when
@@ -6840,7 +6836,7 @@
             onMergedOverrideConfigurationChanged();
         }
 
-        final DisplayContent display = getDisplay();
+        final DisplayContent display = mDisplayContent;
         if (display == null) {
             return;
         }
@@ -7265,7 +7261,7 @@
             final ActivityLifecycleItem lifecycleItem;
             if (andResume) {
                 lifecycleItem = ResumeActivityItem.obtain(
-                        getDisplay().mDisplayContent.isNextTransitionForward());
+                        mDisplayContent.isNextTransitionForward());
             } else {
                 lifecycleItem = PauseActivityItem.obtain();
             }
@@ -7590,11 +7586,7 @@
      * otherwise.
      */
     boolean isFocusedActivityOnDisplay() {
-        final DisplayContent display = getDisplay();
-        if (display == null) {
-            return false;
-        }
-        return display.forAllTaskDisplayAreas(taskDisplayArea ->
+        return mDisplayContent.forAllTaskDisplayAreas(taskDisplayArea ->
                 taskDisplayArea.getFocusedActivity() == this);
     }
 
@@ -7692,6 +7684,7 @@
         if (hasProcess()) {
             proto.write(PROC_ID, app.getPid());
         }
+        proto.write(PIP_AUTO_ENTER_ALLOWED, pictureInPictureArgs.isAutoEnterAllowed());
     }
 
     @Override
@@ -7713,9 +7706,7 @@
     }
 
     void writeNameToProto(ProtoOutputStream proto, long fieldId) {
-        if (appToken != null) {
-            proto.write(fieldId, appToken.getName());
-        }
+        proto.write(fieldId, appToken.getName());
     }
 
     @Override
@@ -7802,42 +7793,40 @@
         /** Gets the horizontal centered container bounds for size compatibility mode. */
         void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation,
                 boolean orientationRequested, boolean canChangeOrientation) {
+            getFrameByOrientation(outBounds, orientation);
             if (mIsFloating) {
-                getFrameByOrientation(outBounds, orientation);
                 outAppBounds.set(outBounds);
                 return;
             }
 
-            if (canChangeOrientation) {
-                getBoundsByRotation(outBounds, rotation);
-                if (orientationRequested) {
-                    getFrameByOrientation(outAppBounds, orientation);
-                } else {
-                    outAppBounds.set(outBounds);
-                }
-            } else {
-                if (orientationRequested) {
-                    getFrameByOrientation(outBounds, orientation);
-                    if ((outBounds.width() > outBounds.height()) != (mWidth > mHeight)) {
-                        // The orientation is mismatched but the display cannot rotate. The bounds
-                        // will fit to the short side of display.
-                        if (orientation == ORIENTATION_LANDSCAPE) {
-                            outBounds.bottom = (int) ((float) mWidth * mWidth / mHeight);
-                            outBounds.right = mWidth;
-                        } else {
-                            outBounds.bottom = mHeight;
-                            outBounds.right = (int) ((float) mHeight * mHeight / mWidth);
-                        }
-                        outBounds.offset(
-                                getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
-                    }
-                } else {
-                    outBounds.set(0, 0, mWidth, mHeight);
-                }
-                outAppBounds.set(outBounds);
-            }
+            getBoundsByRotation(outAppBounds, rotation);
+            final int dW = outAppBounds.width();
+            final int dH = outAppBounds.height();
+            final boolean isOrientationMismatched =
+                    ((outBounds.width() > outBounds.height()) != (dW > dH));
 
-            if (rotation != ROTATION_UNDEFINED) {
+            if (isOrientationMismatched && !canChangeOrientation && orientationRequested) {
+                // The orientation is mismatched but the display cannot rotate. The bounds will fit
+                // to the short side of container.
+                if (orientation == ORIENTATION_LANDSCAPE) {
+                    outBounds.bottom = (int) ((float) dW * dW / dH);
+                    outBounds.right = dW;
+                } else {
+                    outBounds.bottom = dH;
+                    outBounds.right = (int) ((float) dH * dH / dW);
+                }
+                outBounds.offset(getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
+            }
+            outAppBounds.set(outBounds);
+
+            if (isOrientationMismatched) {
+                // One side of container is smaller than the requested size, then it will be scaled
+                // and the final position will be calculated according to the parent container and
+                // scale, so the original size shouldn't be shrunk by insets.
+                final Rect insets = mNonDecorInsets[rotation];
+                outBounds.offset(insets.left, insets.top);
+                outAppBounds.offset(insets.left, insets.top);
+            } else if (rotation != ROTATION_UNDEFINED) {
                 // Ensure the app bounds won't overlap with insets.
                 Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 34f7f79..9df192b7 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -837,7 +837,7 @@
                 final ClientTransaction clientTransaction = ClientTransaction.obtain(
                         proc.getThread(), r.appToken);
 
-                final DisplayContent dc = r.getDisplay().mDisplayContent;
+                final DisplayContent dc = r.mDisplayContent;
                 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index be7a6ae..19755f2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1710,8 +1710,9 @@
         mRootWindowContainer.startPowerModeLaunchIfNeeded(
                 false /* forceSend */, mStartActivity);
 
-        mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(),
-                newTask, mKeepCurTransition, mOptions);
+        mTargetStack.startActivityLocked(mStartActivity,
+                topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask,
+                mKeepCurTransition, mOptions);
         if (mDoResume) {
             final ActivityRecord topTaskActivity =
                     mStartActivity.getTask().topRunningActivityLocked();
@@ -1730,7 +1731,7 @@
                         0 /* configChanges */, !PRESERVE_WINDOWS);
                 // Go ahead and tell window manager to execute app transition for this activity
                 // since the app transition will not be triggered through the resume channel.
-                mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
+                mTargetStack.mDisplayContent.executeAppTransition();
             } else {
                 // If the target stack was not previously focusable (previous top running activity
                 // on that stack was not visible) then any prior calls to move the stack to the
@@ -2480,7 +2481,7 @@
         // to the front if the caller is not itself in the front.
         final boolean differentTopTask;
         if (mTargetStack.getDisplayArea() == mPreferredTaskDisplayArea) {
-            final Task focusStack = mTargetStack.getDisplay().getFocusedStack();
+            final Task focusStack = mTargetStack.mDisplayContent.getFocusedStack();
             final ActivityRecord curTop = (focusStack == null)
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
             final Task topTask = curTop != null ? curTop.getTask() : null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6a8cbfb..080a438 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -125,6 +125,7 @@
 import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -252,7 +253,6 @@
 import com.android.server.AttributeCache;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.SystemServiceManager;
 import com.android.server.UiThread;
 import com.android.server.Watchdog;
@@ -2024,7 +2024,7 @@
 
             if (self.isState(
                     Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
-                self.getDisplay().mDisplayContent.mAppTransition.overridePendingAppTransition(
+                self.mDisplayContent.mAppTransition.overridePendingAppTransition(
                         packageName, enterAnim, exitAnim, null, null);
             }
 
@@ -2408,7 +2408,7 @@
 
                 } else {
                     stack.setWindowingMode(windowingMode);
-                    stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                    stack.mDisplayContent.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
                             true /* notifyClients */);
                 }
                 return true;
@@ -4058,6 +4058,60 @@
                 && r.getRootTask().isInTask(r) != null;
     }
 
+    /**
+     * Puts the given activity in picture in picture mode if possible.
+     *
+     * @return true if the activity is now in picture-in-picture mode, or false if it could not
+     * enter picture-in-picture mode.
+     */
+    boolean enterPictureInPictureMode(ActivityRecord r, final PictureInPictureParams params) {
+        // If the activity is already in picture in picture mode, then just return early
+        if (isInPictureInPictureMode(r)) {
+            return true;
+        }
+
+        // Activity supports picture-in-picture, now check that we can enter PiP at this
+        // point, if it is
+        if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
+                false /* beforeStopping */)) {
+            return false;
+        }
+
+        final Runnable enterPipRunnable = () -> {
+            synchronized (mGlobalLock) {
+                if (r.getParent() == null) {
+                    Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r);
+                    return;
+                }
+                // Only update the saved args from the args that are set
+                r.setPictureInPictureParams(params);
+                final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+                final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
+                mRootWindowContainer.moveActivityToPinnedStack(
+                        r, "enterPictureInPictureMode");
+                final Task stack = r.getRootTask();
+                stack.setPictureInPictureAspectRatio(aspectRatio);
+                stack.setPictureInPictureActions(actions);
+            }
+        };
+
+        if (isKeyguardLocked()) {
+            // If the keyguard is showing or occluded, then try and dismiss it before
+            // entering picture-in-picture (this will prompt the user to authenticate if the
+            // device is currently locked).
+            dismissKeyguard(r.appToken, new KeyguardDismissCallback() {
+                @Override
+                public void onDismissSucceeded() {
+                    mH.post(enterPipRunnable);
+                }
+            }, null /* message */);
+        } else {
+            // Enter picture in picture immediately otherwise
+            enterPipRunnable.run();
+        }
+        return true;
+    }
+
     @Override
     public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
         final long origId = Binder.clearCallingIdentity();
@@ -4065,52 +4119,7 @@
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
                         "enterPictureInPictureMode", token, params);
-
-                // If the activity is already in picture in picture mode, then just return early
-                if (isInPictureInPictureMode(r)) {
-                    return true;
-                }
-
-                // Activity supports picture-in-picture, now check that we can enter PiP at this
-                // point, if it is
-                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
-                        false /* beforeStopping */)) {
-                    return false;
-                }
-
-                final Runnable enterPipRunnable = () -> {
-                    synchronized (mGlobalLock) {
-                        if (r.getParent() == null) {
-                            Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r);
-                            return;
-                        }
-                        // Only update the saved args from the args that are set
-                        r.setPictureInPictureParams(params);
-                        final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
-                        final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
-                        mRootWindowContainer.moveActivityToPinnedStack(
-                                r, "enterPictureInPictureMode");
-                        final Task stack = r.getRootTask();
-                        stack.setPictureInPictureAspectRatio(aspectRatio);
-                        stack.setPictureInPictureActions(actions);
-                    }
-                };
-
-                if (isKeyguardLocked()) {
-                    // If the keyguard is showing or occluded, then try and dismiss it before
-                    // entering picture-in-picture (this will prompt the user to authenticate if the
-                    // device is currently locked).
-                    dismissKeyguard(token, new KeyguardDismissCallback() {
-                        @Override
-                        public void onDismissSucceeded() {
-                            mH.post(enterPipRunnable);
-                        }
-                    }, null /* message */);
-                } else {
-                    // Enter picture in picture immediately otherwise
-                    enterPipRunnable.run();
-                }
-                return true;
+                return enterPictureInPictureMode(r, params);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4175,7 +4184,7 @@
 
         if (params.hasSetAspectRatio()
                 && !mWindowManager.isValidPictureInPictureAspectRatio(
-                        r.getDisplay(), params.getAspectRatio())) {
+                        r.mDisplayContent, params.getAspectRatio())) {
             final float minAspectRatio = mContext.getResources().getFloat(
                     com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
             final float maxAspectRatio = mContext.getResources().getFloat(
@@ -4619,7 +4628,7 @@
             }
             final long origId = Binder.clearCallingIdentity();
             try {
-                display.mDisplayContent.registerRemoteAnimations(definition);
+                display.registerRemoteAnimations(definition);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -4856,6 +4865,11 @@
                             "Requested PIP on an activity that doesn't support it");
                 }
 
+                if (activity.pictureInPictureArgs.isAutoEnterAllowed()) {
+                    enterPictureInPictureMode(activity, activity.pictureInPictureArgs);
+                    return;
+                }
+
                 try {
                     final ClientTransaction transaction = ClientTransaction.obtain(
                             activity.app.getThread(),
@@ -5465,8 +5479,11 @@
         updateResumedAppTrace(r);
         mLastResumedActivity = r;
 
-        r.getDisplay().setFocusedApp(r, true);
-
+        final boolean changed = r.mDisplayContent.setFocusedApp(r);
+        if (changed) {
+            mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+                    true /*updateInputWindows*/);
+        }
         if (prevTask == null || task != prevTask) {
             if (prevTask != null) {
                 mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false);
@@ -6202,12 +6219,10 @@
 
                 // We might change the visibilities here, so prepare an empty app transition which
                 // might be overridden later if we actually change visibilities.
-                final DisplayContent displayContent =
-                        mRootWindowContainer.getDisplayContent(displayId);
-                if (displayContent == null) {
+                final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+                if (dc == null) {
                     return;
                 }
-                final DisplayContent dc = displayContent.mDisplayContent;
                 final boolean wasTransitionSet =
                         dc.mAppTransition.getAppTransition() != TRANSIT_NONE;
                 if (!wasTransitionSet) {
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 9a397fe..01c007e 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -18,6 +18,9 @@
 
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -25,6 +28,8 @@
 import android.window.IDisplayAreaOrganizer;
 import android.window.IDisplayAreaOrganizerController;
 
+import com.android.internal.protolog.common.ProtoLog;
+
 import java.util.HashMap;
 
 public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
@@ -67,9 +72,12 @@
     @Override
     public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) {
         enforceStackPermission("registerOrganizer()");
+        final long uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
                 if (mOrganizersByFeatureIds.get(feature) != null) {
                     throw new IllegalStateException(
                             "Replacing existing organizer currently unsupported");
@@ -96,9 +104,12 @@
     @Override
     public void unregisterOrganizer(IDisplayAreaOrganizer organizer) {
         enforceStackPermission("unregisterTaskOrganizer()");
+        final long uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister display organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
                 mOrganizersByFeatureIds.entrySet().removeIf(
                         entry -> entry.getValue().asBinder() == organizer.asBinder());
 
@@ -113,6 +124,7 @@
     }
 
     void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName());
         try {
             SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl(),
                     "DisplayAreaOrganizerController.onDisplayAreaAppeared");
@@ -123,6 +135,7 @@
     }
 
     void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea vanished name=%s", da.getName());
         try {
             organizer.onDisplayAreaVanished(da.getDisplayAreaInfo());
         } catch (RemoteException e) {
@@ -131,6 +144,7 @@
     }
 
     void onDisplayAreaInfoChanged(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea info changed name=%s", da.getName());
         try {
             organizer.onDisplayAreaInfoChanged(da.getDisplayAreaInfo());
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2f7cc69..77e5736 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -115,13 +115,11 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
 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.REPORT_FOCUS_CHANGE;
 import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
 import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
 import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
@@ -620,6 +618,12 @@
      */
     private boolean mInEnsureActivitiesVisible = false;
 
+    /**
+     * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to
+     * prevent duplicate requests to input.
+     */
+    WindowState mLastRequestedFocus = null;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final ActivityRecord activity = w.mActivityRecord;
@@ -649,8 +653,9 @@
 
     private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
         final ActivityRecord focusedApp = mFocusedApp;
-        ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b",
-                w, w.mAttrs.flags, w.canReceiveKeys());
+        ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
+                w, w.mAttrs.flags, w.canReceiveKeys(),
+                w.canReceiveKeysReason(false /* fromUserTouch */));
 
         if (!w.canReceiveKeys()) {
             return false;
@@ -3072,7 +3077,7 @@
      */
     WindowState findFocusedWindowIfNeeded(int topFocusedDisplayId) {
         return (mWmService.mPerDisplayFocusEnabled || topFocusedDisplayId == INVALID_DISPLAY)
-                ? findFocusedWindow() : null;
+                    ? findFocusedWindow() : null;
     }
 
     WindowState findFocusedWindow() {
@@ -3081,7 +3086,8 @@
         forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
 
         if (mTmpWindow == null) {
-            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows.");
+            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d",
+                    getDisplayId());
             return null;
         }
         return mTmpWindow;
@@ -3116,18 +3122,15 @@
                     && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                 assignWindowLayers(false /* setLayoutNeeded */);
             }
+
+            if (imWindowChanged) {
+                mWmService.mWindowsChanged = true;
+                setLayoutNeeded();
+                newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
+            }
         }
 
-        if (imWindowChanged) {
-            mWmService.mWindowsChanged = true;
-            setLayoutNeeded();
-            newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
-        }
-        if (mCurrentFocus != newFocus) {
-            mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
-        }
-
-        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
+        ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
                 mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
         final WindowState oldFocus = mCurrentFocus;
         mCurrentFocus = newFocus;
@@ -3185,9 +3188,25 @@
         if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
             pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
         }
+
+        // Notify the accessibility manager for the change so it has the windows before the newly
+        // focused one starts firing events.
+        // TODO(b/151179149) investigate what info accessibility service needs before input can
+        // dispatch focus to clients.
+        if (mWmService.mAccessibilityController != null) {
+            mWmService.mH.sendMessage(PooledLambda.obtainMessage(
+                    this::updateAccessibilityOnWindowFocusChanged,
+                    mWmService.mAccessibilityController));
+        }
+
+        mLastFocus = mCurrentFocus;
         return true;
     }
 
+    void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) {
+        accessibilityController.onWindowFocusChangedNotLocked(getDisplayId());
+    }
+
     private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) {
         final Task focusedTask = newFocus != null ? newFocus.getTask() : null;
         final Task unfocusedTask = oldFocus != null ? oldFocus.getTask() : null;
@@ -3219,6 +3238,8 @@
         if (mFocusedApp == newFocus) {
             return false;
         }
+        ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "setFocusedApp %s displayId=%d Callers=%s",
+                newFocus, getDisplayId(), Debug.getCallers(4));
         mFocusedApp = newFocus;
         getInputMonitor().setFocusedAppLw(newFocus);
         updateTouchExcludeRegion();
@@ -3473,7 +3494,11 @@
             return false;
         }
         return mWmService.mDisplayWindowSettings.shouldShowImeLocked(this)
-                || mWmService.mForceDesktopModeOnExternalDisplays;
+                || forceDesktopMode();
+    }
+
+    boolean forceDesktopMode() {
+        return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate();
     }
 
     private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
@@ -4527,7 +4552,7 @@
     boolean supportsSystemDecorations() {
         return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)
                 || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0
-                || mWmService.mForceDesktopModeOnExternalDisplays)
+                || forceDesktopMode())
                 // VR virtual display will be used to run and render 2D app within a VR experience.
                 && mDisplayId != mWmService.mVr2dDisplayId
                 // Do not show system decorations on untrusted virtual display.
@@ -4708,7 +4733,7 @@
         // Traverse all windows top down to assemble the gesture exclusion rects.
         // For each window, we only take the rects that fall within its touchable region.
         forAllWindows(w -> {
-            if (w.cantReceiveTouchInput() || !w.isVisible()
+            if (!w.canReceiveTouchInput() || !w.isVisible()
                     || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
                     || unhandled.isEmpty()) {
                 return;
@@ -5225,30 +5250,6 @@
                 && (mAtmService.mRunningVoice == null);
     }
 
-    void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
-        final ActivityRecord newFocus;
-        final IBinder token = r.appToken;
-        if (token == null) {
-            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Clearing focused app, displayId=%d",
-                    mDisplayId);
-            newFocus = null;
-        } else {
-            newFocus = mWmService.mRoot.getActivityRecord(token);
-            if (newFocus == null) {
-                Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
-                        + ", displayId=" + mDisplayId);
-            }
-            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
-                    "Set focused app to: %s moveFocusNow=%b displayId=%d", newFocus,
-                    moveFocusNow, mDisplayId);
-        }
-
-        final boolean changed = setFocusedApp(newFocus);
-        if (moveFocusNow && changed) {
-            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
-                    true /*updateInputWindows*/);
-        }
-    }
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 1c147c2..779f6b2 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -29,6 +29,7 @@
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -38,11 +39,6 @@
 import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_TOP_GESTURES;
 import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
@@ -57,20 +53,18 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
@@ -78,27 +72,21 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
@@ -171,7 +159,6 @@
 import android.view.PointerIcon;
 import android.view.Surface;
 import android.view.View;
-import android.view.ViewRootImpl;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Side.InsetsSide;
 import android.view.WindowInsets.Type;
@@ -913,10 +900,6 @@
                         (int) attrs.hideTimeoutMilliseconds,
                         AccessibilityManager.FLAG_CONTENT_TEXT);
                 attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
-                // Toast can show with below conditions when the screen is locked.
-                if (canToastShowWhenLocked(callingPid)) {
-                    attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-                }
                 // Toasts can't be clickable
                 attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                 break;
@@ -947,16 +930,6 @@
     }
 
     /**
-     * @return {@code true} if the calling activity initiate toast and is visible with
-     * {@link WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} flag.
-     */
-    boolean canToastShowWhenLocked(int callingPid) {
-        return mDisplayContent.forAllWindows(w -> {
-            return callingPid == w.mSession.mPid && w.isVisible() && w.canShowWhenLocked();
-        }, true /* traverseTopToBottom */);
-    }
-
-    /**
      * Check if a window can be added to the system.
      *
      * Currently enforces that two window types are singletons per display:
@@ -1440,8 +1413,7 @@
             DisplayCutout.ParcelableWrapper outDisplayCutout) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
         final int pfl = attrs.privateFlags;
-        final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
-        final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs);
+        final int sysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
 
         final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
         final boolean layoutInScreenAndInsetDecor = layoutInScreen
@@ -1461,7 +1433,7 @@
                 : mDisplayContent.mDisplayFrames;
 
         if (layoutInScreenAndInsetDecor && !screenDecor) {
-            if ((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
+            if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                     || (attrs.getFitInsetsTypes() & Type.navigationBars()) == 0) {
                 outFrame.set(displayFrames.mUnrestricted);
             } else {
@@ -1492,7 +1464,6 @@
             InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets);
             outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame)
                     .getDisplayCutout());
-            return mForceShowSystemBars;
         } else {
             if (layoutInScreen) {
                 outFrame.set(displayFrames.mUnrestricted);
@@ -1506,22 +1477,8 @@
             outContentInsets.setEmpty();
             outStableInsets.setEmpty();
             outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
-            return mForceShowSystemBars;
         }
-    }
-
-    // TODO(b/118118435): remove after migration
-    private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) {
-        int impliedFlags = 0;
-        final boolean forceWindowDrawsBarBackgrounds =
-                (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
-                        && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT;
-        if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
-                || forceWindowDrawsBarBackgrounds) {
-            impliedFlags |= SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
-            impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        }
-        return impliedFlags;
+        return mForceShowSystemBars;
     }
 
     private final Runnable mClearHideNavigationFlag = new Runnable() {
@@ -1557,7 +1514,7 @@
                             if (mInputConsumer == null) {
                                 return;
                             }
-                            showNavigationBar();
+                            showSystemBars();
                             // Any user activity always causes us to show the
                             // navigation controls, if they had been hidden.
                             // We also clear the low profile and only content
@@ -1592,13 +1549,13 @@
             }
         }
 
-        private void showNavigationBar() {
+        private void showSystemBars() {
             final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController()
                     .peekSourceProvider(ITYPE_NAVIGATION_BAR);
             final InsetsControlTarget target =
                     provider != null ? provider.getControlTarget() : null;
             if (target != null) {
-                target.showInsets(Type.navigationBars(), false /* fromIme */);
+                target.showInsets(Type.systemBars(), false /* fromIme */);
             }
         }
     }
@@ -1668,11 +1625,8 @@
         final int behavior = mLastBehavior;
         final InsetsSourceProvider provider =
                 mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR);
-        boolean navVisible = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
-                ? (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
-                : provider != null
-                        ? provider.isClientVisible()
-                        : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
+        boolean navVisible = provider != null ? provider.isClientVisible()
+                : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
         boolean navTranslucent = (sysui
                 & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
         boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0
@@ -2057,80 +2011,6 @@
         return mNavigationBarController.checkHiddenLw();
     }
 
-    private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
-            boolean insetDecors, Rect pf, Rect df, Rect cf, Rect vf,
-            DisplayFrames displayFrames) {
-        if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) {
-            // Here's a special case: if the child window is not the 'dock window'
-            // or input method target, and the window it is attached to is below
-            // the dock window, then the frames we computed for the window it is
-            // attached to can not be used because the dock is effectively part
-            // of the underlying window and the attached window is floating on top
-            // of the whole thing. So, we ignore the attached window and explicitly
-            // compute the frames that would be appropriate without the dock.
-            vf.set(displayFrames.mDock);
-            cf.set(displayFrames.mDock);
-            df.set(displayFrames.mDock);
-        } else {
-
-            // In case we forced the window to draw behind the navigation bar, restrict df to
-            // DF.Restricted to simulate old compat behavior.
-            Rect parentDisplayFrame = attached.getDisplayFrame();
-            final WindowManager.LayoutParams attachedAttrs = attached.mAttrs;
-            if ((attachedAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
-                    && (attachedAttrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
-                    && (attachedAttrs.systemUiVisibility
-                            & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) {
-                parentDisplayFrame = new Rect(parentDisplayFrame);
-                parentDisplayFrame.intersect(displayFrames.mRestricted);
-            }
-
-            // The effective display frame of the attached window depends on whether it is taking
-            // care of insetting its content. If not, we need to use the parent's content frame so
-            // that the entire window is positioned within that content. Otherwise we can use the
-            // parent display frame and let the attached window take care of positioning its content
-            // appropriately.
-            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                // Set the content frame of the attached window to the parent's decor frame
-                // (same as content frame when IME isn't present) if specifically requested by
-                // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag.
-                // Otherwise, use the overscan frame.
-                cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
-                        ? attached.getContentFrame() : parentDisplayFrame);
-            } else {
-                // If the window is resizing, then we want to base the content frame on our attached
-                // content frame to resize...however, things can be tricky if the attached window is
-                // NOT in resize mode, in which case its content frame will be larger.
-                // Ungh. So to deal with that, make sure the content frame we end up using is not
-                // covering the IM dock.
-                cf.set(attached.getContentFrame());
-                if (attached.isVoiceInteraction()) {
-                    cf.intersectUnchecked(displayFrames.mVoiceContent);
-                } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) {
-                    cf.intersectUnchecked(displayFrames.mContent);
-                }
-            }
-            df.set(insetDecors ? parentDisplayFrame : cf);
-            vf.set(attached.getVisibleFrame());
-        }
-        // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
-        // positioned relative to its parent or the entire screen.
-        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
-    }
-
-    private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
-        if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
-            return;
-        }
-        // If app is requesting a stable layout, don't let the content insets go below the stable
-        // values.
-        if ((fl & FLAG_FULLSCREEN) != 0) {
-            r.intersectUnchecked(displayFrames.mStableFullscreen);
-        } else {
-            r.intersectUnchecked(displayFrames.mStable);
-        }
-    }
-
     private boolean canReceiveInput(WindowState win) {
         boolean notFocusable =
                 (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
@@ -2165,8 +2045,6 @@
         final int fl = PolicyControl.getWindowFlags(win, attrs);
         final int pfl = attrs.privateFlags;
         final int sim = attrs.softInputMode;
-        final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
-        final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
 
         displayFrames = win.getDisplayFrames(displayFrames);
         final WindowFrames windowFrames = win.getWindowFrames();
@@ -2191,351 +2069,60 @@
 
         sf.set(displayFrames.mStable);
 
-        if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
-            final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
-            final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
-            final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
-            getRotatedWindowBounds(displayFrames, win, sTmpRect);
-            final Rect dfu = sTmpRect;
-            Insets insets = Insets.of(0, 0, 0, 0);
-            for (int i = types.size() - 1; i >= 0; i--) {
-                final InsetsSource source = mDisplayContent.getInsetsPolicy()
-                        .getInsetsForDispatch(win).peekSource(types.valueAt(i));
-                if (source == null) {
-                    continue;
-                }
-                insets = Insets.max(insets, source.calculateInsets(
-                        dfu, attrs.isFitInsetsIgnoringVisibility()));
+        final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
+        final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
+        final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
+        getRotatedWindowBounds(displayFrames, win, sTmpRect);
+        final Rect dfu = sTmpRect;
+        Insets insets = Insets.of(0, 0, 0, 0);
+        for (int i = types.size() - 1; i >= 0; i--) {
+            final InsetsSource source = mDisplayContent.getInsetsPolicy()
+                    .getInsetsForDispatch(win).peekSource(types.valueAt(i));
+            if (source == null) {
+                continue;
             }
-            final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
-            final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
-            final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
-            final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
-            df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
-            if (attached == null) {
-                pf.set(df);
-                vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING
-                        ? displayFrames.mCurrent : displayFrames.mDock);
-            } else {
-                pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
-                vf.set(attached.getVisibleFrame());
-            }
-            cf.set(adjust != SOFT_INPUT_ADJUST_RESIZE
-                    ? displayFrames.mDock : displayFrames.mContent);
-            dcf.set(displayFrames.mSystem);
-        } else if (type == TYPE_INPUT_METHOD) {
-            vf.set(displayFrames.mDock);
-            cf.set(displayFrames.mDock);
-            df.set(displayFrames.mDock);
-            pf.set(displayFrames.mDock);
-            // IM dock windows layout below the nav bar...
-            pf.bottom = df.bottom = displayFrames.mUnrestricted.bottom;
-            // ...with content insets above the nav bar
-            cf.bottom = vf.bottom = displayFrames.mStable.bottom;
-            if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
-                // The status bar forces the navigation bar while it's visible. Make sure the IME
-                // avoids the navigation bar in that case.
-                if (mNavigationBarPosition == NAV_BAR_RIGHT) {
-                    pf.right = df.right = cf.right = vf.right =
-                            displayFrames.mStable.right;
-                } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
-                    pf.left = df.left = cf.left = vf.left = displayFrames.mStable.left;
-                }
-            }
-
-            // In case the navigation bar is on the bottom, we use the frame height instead of the
-            // regular height for the insets we send to the IME as we need some space to show
-            // additional buttons in SystemUI when the IME is up.
-            if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
-                final int rotation = displayFrames.mRotation;
-                final int uimode = mService.mPolicy.getUiMode();
-                final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode)
-                        - getNavigationBarHeight(rotation, uimode);
-                if (navHeightOffset > 0) {
-                    cf.bottom -= navHeightOffset;
-                    sf.bottom -= navHeightOffset;
-                    vf.bottom -= navHeightOffset;
-                    dcf.bottom -= navHeightOffset;
-                }
-            }
-
-            // IM dock windows always go to the bottom of the screen.
-            attrs.gravity = Gravity.BOTTOM;
-        } else if (type == TYPE_VOICE_INTERACTION) {
-            df.set(displayFrames.mUnrestricted);
-            pf.set(displayFrames.mUnrestricted);
-            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                cf.set(displayFrames.mDock);
-            } else {
-                cf.set(displayFrames.mContent);
-            }
-            if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                vf.set(displayFrames.mCurrent);
-            } else {
-                vf.set(cf);
-            }
-        } else if (type == TYPE_WALLPAPER) {
-            layoutWallpaper(displayFrames, pf, df, cf);
-        } else if (win == mStatusBar || type == TYPE_NOTIFICATION_SHADE) {
-            df.set(displayFrames.mUnrestricted);
-            pf.set(displayFrames.mUnrestricted);
-            cf.set(displayFrames.mStable);
-            vf.set(displayFrames.mStable);
-
-            if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
-                // cf.bottom should not be below the stable bottom, or the content might be obscured
-                // by the navigation bar.
-                if (cf.bottom > displayFrames.mContent.bottom) {
-                    cf.bottom = displayFrames.mContent.bottom;
-                }
-            } else {
-                if (cf.bottom > displayFrames.mDock.bottom) {
-                    cf.bottom = displayFrames.mDock.bottom;
-                }
-                if (vf.bottom > displayFrames.mContent.bottom) {
-                    vf.bottom = displayFrames.mContent.bottom;
-                }
-            }
-        } else {
-            dcf.set(displayFrames.mSystem);
-            final boolean isAppWindow =
-                    type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
-            final boolean topAtRest =
-                    win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
-            if (isAppWindow && !topAtRest) {
-                if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
-                        && (fl & FLAG_FULLSCREEN) == 0
-                        && (fl & FLAG_TRANSLUCENT_STATUS) == 0
-                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
-                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {
-                    // Ensure policy decor includes status bar
-                    dcf.top = displayFrames.mStable.top;
-                }
-                if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
-                        && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
-                        && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
-                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) {
-                    // Ensure policy decor includes navigation bar
-                    dcf.bottom = displayFrames.mStable.bottom;
-                    dcf.right = displayFrames.mStable.right;
-                }
-            }
-
-            if (layoutInScreen && layoutInsetDecor) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
-                        + "): IN_SCREEN, INSET_DECOR");
-                // This is the case for a normal activity window: we want it to cover all of the
-                // screen space, and it can take care of moving its contents to account for screen
-                // decorations that intrude into that space.
-                if (attached != null) {
-                    // If this window is attached to another, our display
-                    // frame is the same as the one we are attached to.
-                    setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, cf, vf,
-                            displayFrames);
-                } else {
-                    if (type == TYPE_STATUS_BAR_ADDITIONAL || type == TYPE_STATUS_BAR_SUB_PANEL) {
-                        // Status bar panels are the only windows who can go on top of the status
-                        // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
-                        // have the same privileges as the status bar itself.
-                        //
-                        // However, they should still dodge the navigation bar if it exists.
-
-                        pf.left = df.left = hasNavBar
-                                ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
-                        pf.top = df.top = displayFrames.mUnrestricted.top;
-                        pf.right = df.right = hasNavBar
-                                ? displayFrames.mRestricted.right
-                                : displayFrames.mUnrestricted.right;
-                        pf.bottom = df.bottom = hasNavBar
-                                ? displayFrames.mRestricted.bottom
-                                : displayFrames.mUnrestricted.bottom;
-
-                        if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf);
-                    } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                            && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW
-                            || type == TYPE_VOLUME_OVERLAY
-                            || type == TYPE_KEYGUARD_DIALOG)) {
-                        // Asking for layout as if the nav bar is hidden, lets the application
-                        // extend into the unrestricted overscan screen area. We only do this for
-                        // application windows and certain system windows to ensure no window that
-                        // can be above the nav bar can do this.
-                        df.set(displayFrames.mUnrestricted);
-                        pf.set(displayFrames.mUnrestricted);
-                    } else {
-                        df.set(displayFrames.mRestricted);
-                        pf.set(displayFrames.mRestricted);
-                    }
-
-                    if ((fl & FLAG_FULLSCREEN) == 0) {
-                        if (win.isVoiceInteraction()) {
-                            cf.set(displayFrames.mVoiceContent);
-                        } else {
-                            // IME Insets are handled on the client for ADJUST_RESIZE in the new
-                            // insets world
-                            if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE
-                                    || adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                                cf.set(displayFrames.mDock);
-                            } else {
-                                cf.set(displayFrames.mContent);
-                            }
-                        }
-                    } else {
-                        // Full screen windows are always given a layout that is as if the status
-                        // bar and other transient decors are gone. This is to avoid bad states when
-                        // moving from a window that is not hiding the status bar to one that is.
-                        cf.set(displayFrames.mRestricted);
-                    }
-                    applyStableConstraints(sysUiFl, fl, cf, displayFrames);
-                    if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
-                            && adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.set(displayFrames.mCurrent);
-                    } else {
-                        vf.set(cf);
-                    }
-                }
-            } else if (layoutInScreen || (sysUiFl
-                    & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                    | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
-                        + "): IN_SCREEN");
-                // A window that has requested to fill the entire screen just
-                // gets everything, period.
-                if (type == TYPE_STATUS_BAR_ADDITIONAL || type == TYPE_STATUS_BAR_SUB_PANEL) {
-                    cf.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                    if (hasNavBar) {
-                        pf.left = df.left = cf.left = displayFrames.mDock.left;
-                        pf.right = df.right = cf.right = displayFrames.mRestricted.right;
-                        pf.bottom = df.bottom = cf.bottom =
-                                displayFrames.mRestricted.bottom;
-                    }
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf);
-                } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
-                    // The navigation bar has Real Ultimate Power.
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                    if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf);
-                } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
-                        && ((fl & FLAG_FULLSCREEN) != 0)) {
-                    // Fullscreen secure system overlays get what they ask for. Screenshot region
-                    // selection overlay should also expand to full screen.
-                    cf.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                } else if (type == TYPE_BOOT_PROGRESS) {
-                    // Boot progress screen always covers entire display.
-                    cf.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                        && (type == TYPE_NOTIFICATION_SHADE
-                        || type == TYPE_TOAST
-                        || type == TYPE_DOCK_DIVIDER
-                        || type == TYPE_VOICE_INTERACTION_STARTING
-                        || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
-                    // Asking for layout as if the nav bar is hidden, lets the
-                    // application extend into the unrestricted screen area.  We
-                    // only do this for application windows (or toasts) to ensure no window that
-                    // can be above the nav bar can do this.
-                    // XXX This assumes that an app asking for this will also
-                    // ask for layout in only content.  We can't currently figure out
-                    // what the screen would be if only laying out to hide the nav bar.
-                    cf.set(displayFrames.mUnrestricted);
-                    df.set(displayFrames.mUnrestricted);
-                    pf.set(displayFrames.mUnrestricted);
-                } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
-
-                    // IME Insets are handled on the client for ADJUST_RESIZE in the new insets
-                    // world
-                    if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE
-                            || adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        cf.set(displayFrames.mDock);
-                    } else {
-                        cf.set(displayFrames.mContent);
-                    }
-                } else {
-                    cf.set(displayFrames.mRestricted);
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
-                }
-
-                applyStableConstraints(sysUiFl, fl, cf, displayFrames);
-
-                if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
-                        && adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                    vf.set(displayFrames.mCurrent);
-                } else {
-                    vf.set(cf);
-                }
-            } else if (attached != null) {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
-                        + "): attached to " + attached);
-                // A child window should be placed inside of the same visible
-                // frame that its parent had.
-                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, cf, vf,
-                        displayFrames);
-            } else {
-                if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
-                        + "): normal window");
-                // Otherwise, a normal window must be placed inside the content
-                // of all screen decorations.
-                if (type == TYPE_STATUS_BAR_ADDITIONAL) {
-                    // Status bar panels can go on
-                    // top of the status bar. They are protected by the STATUS_BAR_SERVICE
-                    // permission, so they have the same privileges as the status bar itself.
-                    cf.set(displayFrames.mRestricted);
-                    df.set(displayFrames.mRestricted);
-                    pf.set(displayFrames.mRestricted);
-                } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
-                    // These dialogs are stable to interim decor changes.
-                    cf.set(displayFrames.mStable);
-                    df.set(displayFrames.mStable);
-                    pf.set(displayFrames.mStable);
-                } else {
-                    pf.set(displayFrames.mContent);
-                    if (win.isVoiceInteraction()) {
-                        cf.set(displayFrames.mVoiceContent);
-                        df.set(displayFrames.mVoiceContent);
-                    } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
-                        cf.set(displayFrames.mDock);
-                        df.set(displayFrames.mDock);
-                    } else {
-                        cf.set(displayFrames.mContent);
-                        df.set(displayFrames.mContent);
-                    }
-                    if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
-                            && adjust != SOFT_INPUT_ADJUST_NOTHING) {
-                        vf.set(displayFrames.mCurrent);
-                    } else {
-                        vf.set(cf);
-                    }
-                }
-            }
+            insets = Insets.max(insets, source.calculateInsets(
+                    dfu, attrs.isFitInsetsIgnoringVisibility()));
         }
+        final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
+        final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
+        final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
+        final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
+        df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
+        if (attached == null) {
+            pf.set(df);
+            if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
+                final InsetsSource source = mDisplayContent.getInsetsPolicy()
+                        .getInsetsForDispatch(win).peekSource(ITYPE_IME);
+                if (source != null) {
+                    pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */));
+                }
+            }
+            vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING
+                    ? displayFrames.mCurrent : displayFrames.mDock);
+        } else {
+            pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
+            vf.set(attached.getVisibleFrame());
+        }
+        cf.set(adjust != SOFT_INPUT_ADJUST_RESIZE
+                ? displayFrames.mDock : displayFrames.mContent);
+        dcf.set(displayFrames.mSystem);
 
         final int cutoutMode = attrs.layoutInDisplayCutoutMode;
-        final boolean attachedInParent = attached != null && !layoutInScreen;
-        final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
-                || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
-                || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
-                        && !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
-                                ITYPE_STATUS_BAR));
-        final boolean requestedHideNavigation =
-                (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
-                || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
-                        && !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
-                                ITYPE_NAVIGATION_BAR));
-
-        // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
-        // cropped / shifted to the displayFrame in WindowState.
-        final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
-                && type != TYPE_BASE_APPLICATION;
         // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
         // the cutout safe zone.
         if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+            final boolean attachedInParent = attached != null && !layoutInScreen;
+            final InsetsState requestedInsetsState = win.getRequestedInsetsState();
+            final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
+                    || !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR);
+            final boolean requestedHideNavigation =
+                    !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
+
+            // TYPE_BASE_APPLICATION windows are never considered floating here because they don't
+            // get cropped / shifted to the displayFrame in WindowState.
+            final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
+                    && type != TYPE_BASE_APPLICATION;
             final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
             displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
             if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
@@ -2631,13 +2218,6 @@
         }
     }
 
-    private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect cf) {
-        // The wallpaper has Real Ultimate Power
-        df.set(displayFrames.mUnrestricted);
-        pf.set(displayFrames.mUnrestricted);
-        cf.set(displayFrames.mUnrestricted);
-    }
-
     private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
         final int rotation = displayFrames.mRotation;
         final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth,
@@ -3343,54 +2923,37 @@
             // Swipe-up for navigation bar is disabled during setup
             return;
         }
-        if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
-            final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider();
-            final InsetsControlTarget controlTarget = provider != null
-                    ? provider.getControlTarget() : null;
+        final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider();
+        final InsetsControlTarget controlTarget = provider != null
+                ? provider.getControlTarget() : null;
 
-            if (controlTarget == null || controlTarget == getNotificationShade()) {
-                // No transient mode on lockscreen (in notification shade window).
-                return;
-            }
+        if (controlTarget == null || controlTarget == getNotificationShade()) {
+            // No transient mode on lockscreen (in notification shade window).
+            return;
+        }
 
-            final InsetsState requestedState = controlTarget.getRequestedInsetsState();
-            final @InsetsType int restorePositionTypes =
-                    (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
-                            ? Type.navigationBars() : 0)
-                    | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
-                            ? Type.statusBars() : 0);
+        final InsetsState requestedState = controlTarget.getRequestedInsetsState();
+        final @InsetsType int restorePositionTypes = (requestedState.getSourceOrDefaultVisibility(
+                ITYPE_NAVIGATION_BAR) ? Type.navigationBars() : 0) | (
+                requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) ? Type.statusBars()
+                        : 0);
 
-            if (swipeTarget == mNavigationBar
-                    && (restorePositionTypes & Type.navigationBars()) != 0) {
-                // Don't show status bar when swiping on already visible navigation bar.
-                // But restore the position of navigation bar if it has been moved by the control
-                // target.
-                controlTarget.showInsets(Type.navigationBars(), false);
-                return;
-            }
+        if (swipeTarget == mNavigationBar
+                && (restorePositionTypes & Type.navigationBars()) != 0) {
+            // Don't show status bar when swiping on already visible navigation bar.
+            // But restore the position of navigation bar if it has been moved by the control
+            // target.
+            controlTarget.showInsets(Type.navigationBars(), false);
+            return;
+        }
 
-            if (controlTarget.canShowTransient()) {
-                // Show transient bars if they are hidden; restore position if they are visible.
-                mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE);
-                controlTarget.showInsets(restorePositionTypes, false);
-            } else {
-                // Restore visibilities and positions of system bars.
-                controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false);
-            }
+        if (controlTarget.canShowTransient()) {
+            // Show transient bars if they are hidden; restore position if they are visible.
+            mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE);
+            controlTarget.showInsets(restorePositionTypes, false);
         } else {
-            boolean sb = mStatusBarController.checkShowTransientBarLw();
-            boolean nb = mNavigationBarController.checkShowTransientBarLw()
-                    && !isNavBarEmpty(mLastSystemUiFlags);
-            if (sb || nb) {
-                // Don't show status bar when swiping on already visible navigation bar
-                if (!nb && swipeTarget == mNavigationBar) {
-                    if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
-                    return;
-                }
-                if (sb) mStatusBarController.showTransient();
-                if (nb) mNavigationBarController.showTransient();
-                updateSystemUiVisibilityLw();
-            }
+            // Restore visibilities and positions of system bars.
+            controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false);
         }
         mImmersiveModeConfirmation.confirmCurrentPrompt();
     }
@@ -3479,11 +3042,10 @@
                 navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
         final int opaqueAppearance = InsetsFlags.getAppearance(visibility)
                 & (APPEARANCE_OPAQUE_STATUS_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS);
-        final int appearance = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
-                ? updateLightNavigationBarAppearanceLw(win.mAttrs.insetsFlags.appearance,
-                        mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
-                        mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance
-                : InsetsFlags.getAppearance(visibility);
+        final int appearance = updateLightNavigationBarAppearanceLw(
+                win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
+                mTopFullscreenOpaqueOrDimmingWindowState,
+                mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
         final int diff = visibility ^ mLastSystemUiFlags;
         final InsetsPolicy insetsPolicy = getInsetsPolicy();
         final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
@@ -3805,9 +3367,8 @@
         vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
 
         // update navigation bar
-        boolean newInsetsMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL;
-        boolean oldImmersiveMode = newInsetsMode ? mLastImmersiveMode : isImmersiveMode(oldVis);
-        boolean newImmersiveMode = newInsetsMode ? isImmersiveMode(win) : isImmersiveMode(vis);
+        boolean oldImmersiveMode = mLastImmersiveMode;
+        boolean newImmersiveMode = isImmersiveMode(win);
         if (oldImmersiveMode != newImmersiveMode) {
             mLastImmersiveMode = newImmersiveMode;
             final String pkg = win.getOwningPackage();
@@ -3911,15 +3472,6 @@
         }
     }
 
-    // TODO(b/118118435): Remove this after migration
-    private boolean isImmersiveMode(int vis) {
-        final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-        return getNavigationBar() != null
-                && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
-                && (vis & flags) != 0
-                && canHideNavigationBar();
-    }
-
     private boolean isImmersiveMode(WindowState win) {
         if (win == null) {
             return false;
@@ -4110,9 +3662,7 @@
 
         mPointerLocationView = new PointerLocationView(mContext);
         mPointerLocationView.setPrintCoords(false);
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                WindowManager.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.MATCH_PARENT);
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
         lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
         lp.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 0206787..0f43e49 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -330,10 +330,8 @@
         // It's also not likely to rotate a TV screen.
         final boolean isTv = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK);
-        final boolean forceDesktopMode =
-                mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay;
         mDefaultFixedToUserRotation =
-                (isCar || isTv || mService.mIsPc || forceDesktopMode)
+                (isCar || isTv || mService.mIsPc || mDisplayContent.forceDesktopMode())
                 // For debug purposes the next line turns this feature off with:
                 // $ adb shell setprop config.override_forced_orient true
                 // $ adb shell wm size reset
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index df7c070..c8c83a6 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -248,7 +248,7 @@
         writeSettingsIfNeeded(entry, displayInfo);
     }
 
-    private int getWindowingModeLocked(Entry entry, int displayId) {
+    private int getWindowingModeLocked(Entry entry, DisplayContent dc) {
         int windowingMode = entry != null ? entry.mWindowingMode
                 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         // This display used to be in freeform, but we don't support freeform anymore, so fall
@@ -259,10 +259,8 @@
         }
         // No record is present so use default windowing mode policy.
         if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
-            final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
-                    && displayId != Display.DEFAULT_DISPLAY;
             windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement
-                    && (mService.mIsPc || forceDesktopMode)
+                    && (mService.mIsPc || dc.forceDesktopMode())
                     ? WindowConfiguration.WINDOWING_MODE_FREEFORM
                     : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
         }
@@ -272,7 +270,7 @@
     int getWindowingModeLocked(DisplayContent dc) {
         final DisplayInfo displayInfo = dc.getDisplayInfo();
         final Entry entry = getEntry(displayInfo);
-        return getWindowingModeLocked(entry, dc.getDisplayId());
+        return getWindowingModeLocked(entry, dc);
     }
 
     void setWindowingModeLocked(DisplayContent dc, int mode) {
@@ -382,7 +380,7 @@
         final Entry entry = getOrCreateEntry(displayInfo);
 
         // Setting windowing mode first, because it may override overscan values later.
-        dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId()));
+        dc.setWindowingMode(getWindowingModeLocked(entry, dc));
 
         dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
                 entry.mUserRotation, entry.mFixedToUserRotation);
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 999aab9..ec62ed4 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -114,7 +114,7 @@
 
                     final WindowState callingWin = mService.windowForClientLocked(
                             null, window, false);
-                    if (callingWin == null || callingWin.cantReceiveTouchInput()) {
+                    if (callingWin == null || !callingWin.canReceiveTouchInput()) {
                         Slog.w(TAG_WM, "Bad requesting window " + window);
                         return null;  // !!! TODO: throw here?
                     }
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index e166bfc..0978636 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -24,6 +24,7 @@
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.input.InputManagerService;
 import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;
@@ -252,7 +253,7 @@
         // All the calls below need to happen without the WM lock held since they call into AM.
         mService.mAtmInternal.saveANRState(reason);
 
-        if (activity != null && activity.appToken != null) {
+        if (activity != null) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
             final boolean abort = activity.keyDispatchingTimedOut(reason, windowPid);
@@ -410,6 +411,8 @@
             requestRefreshConfiguration = dispatchPointerCaptureChanged(focusedWindow, false);
         }
         mFocusedWindow.set(newFocusedWindow);
+        mService.mH.sendMessage(PooledLambda.obtainMessage(mService::reportFocusChanged,
+                oldToken, newToken));
         return requestRefreshConfiguration;
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index fb511e0..c493b66 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -275,7 +275,7 @@
 
     void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
             final WindowState child, int flags, final int type, final boolean isVisible,
-            final boolean hasFocus, final boolean hasWallpaper) {
+            final boolean focusable, final boolean hasWallpaper) {
         // Add a window to our list of input windows.
         inputWindowHandle.name = child.toString();
         flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
@@ -283,7 +283,7 @@
         inputWindowHandle.layoutParamsType = type;
         inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
         inputWindowHandle.visible = isVisible;
-        inputWindowHandle.focusable = hasFocus;
+        inputWindowHandle.focusable = focusable;
         inputWindowHandle.hasWallpaper = hasWallpaper;
         inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
         inputWindowHandle.ownerPid = child.mSession.mPid;
@@ -370,7 +370,8 @@
      * Layer assignment is assumed to be complete by the time this is called.
      */
     public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
-        ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s", newWindow);
+        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d",
+                newWindow, mDisplayId);
 
         if (newWindow != mInputFocus) {
             if (newWindow != null && newWindow.canReceiveKeys()) {
@@ -471,8 +472,9 @@
 
             resetInputConsumers(mInputTransaction);
 
-            mDisplayContent.forAllWindows(this,
-                    true /* traverseTopToBottom */);
+            mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
+
+            updateInputFocusRequest();
 
             if (!mUpdateInputWindowsImmediately) {
                 mDisplayContent.getPendingTransaction().merge(mInputTransaction);
@@ -482,6 +484,29 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
+        private void updateInputFocusRequest() {
+            if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) {
+                return;
+            }
+
+            final WindowState focus = mDisplayContent.mCurrentFocus;
+            if (focus == null || focus.mInputWindowHandle.token == null) {
+                mDisplayContent.mLastRequestedFocus = focus;
+                return;
+            }
+
+            if (!focus.mWinAnimator.hasSurface()) {
+                ProtoLog.d(WM_DEBUG_FOCUS_LIGHT,
+                        "Focus not requested for window=%s because it has no surface",
+                        focus);
+                return;
+            }
+
+            mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId);
+            mDisplayContent.mLastRequestedFocus = focus;
+            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
+        }
+
         @Override
         public void accept(WindowState w) {
             final InputChannel inputChannel = w.mInputChannel;
@@ -493,7 +518,7 @@
             final int type = w.mAttrs.type;
             final boolean isVisible = w.isVisibleLw();
             if (inputChannel == null || inputWindowHandle == null || w.mRemoved
-                    || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
+                    || (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
                 if (w.mWinAnimator.hasSurface()) {
                     // Assign an InputInfo with type to the overlay window which can't receive input
                     // event. This is used to omit Surfaces from occlusion detection.
@@ -509,11 +534,12 @@
 
             final int flags = w.mAttrs.flags;
             final int privateFlags = w.mAttrs.privateFlags;
-            final boolean hasFocus = w.isFocused();
+            final boolean focusable = w.canReceiveKeys()
+                    && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
 
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
-                        mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
+                        mRecentsAnimationInputConsumer.mWindowHandle, focusable)) {
                     mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                     mAddRecentsAnimationInputConsumerHandle = false;
                 }
@@ -558,7 +584,7 @@
             }
 
             populateInputWindowHandle(
-                    inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+                    inputWindowHandle, w, flags, type, isVisible, focusable, hasWallpaper);
 
             // register key interception info
             mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token,
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c36a75b..69e8c57 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -190,7 +190,7 @@
                     mAodShowing ? 1 : 0,
                     1 /* keyguardGoingAway */,
                     "keyguardGoingAway");
-            mRootWindowContainer.getDefaultDisplay().mDisplayContent
+            mRootWindowContainer.getDefaultDisplay()
                     .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                             false /* alwaysKeepCurrent */, convertTransitFlags(flags),
                             false /* forceOverride */);
@@ -314,7 +314,7 @@
         if (isKeyguardLocked()) {
             mService.deferWindowLayout();
             try {
-                mRootWindowContainer.getDefaultDisplay().mDisplayContent
+                mRootWindowContainer.getDefaultDisplay()
                         .prepareAppTransition(resolveOccludeTransit(),
                                 false /* alwaysKeepCurrent */, 0 /* flags */,
                                 true /* forceOverride */);
@@ -344,8 +344,7 @@
 
         // If we are about to unocclude the Keyguard, but we can dismiss it without security,
         // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
-        final DisplayContent dc =
-                mRootWindowContainer.getDefaultDisplay().mDisplayContent;
+        final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
         if (mKeyguardShowing && canDismissKeyguard()
                 && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
             dc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
@@ -368,7 +367,7 @@
     }
 
     private int resolveOccludeTransit() {
-        final DisplayContent dc = mRootWindowContainer.getDefaultDisplay().mDisplayContent;
+        final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
                 && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
                 // TODO(b/113840485): Handle app transition for individual display.
@@ -485,7 +484,7 @@
             }
             // TODO(b/123372519): isShowingDream can only works on default display.
             if (mDisplayId == DEFAULT_DISPLAY) {
-                mOccluded |= mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent
+                mOccluded |= mService.mRootWindowContainer.getDefaultDisplay()
                         .getDisplayPolicy().isShowingDreamLw();
             }
 
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index c8d7693..c496901 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -619,7 +619,7 @@
             mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities();
             final Task rootTask = task.getRootTask();
             if (rootTask != null) {
-                rootTask.getDisplay().mDisplayContent.executeAppTransition();
+                rootTask.mDisplayContent.executeAppTransition();
             }
         } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
             mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 4be4c89..1077736 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -11,3 +11,4 @@
 riddlehsu@google.com
 louischang@google.com
 winsonc@google.com
+tigerhuang@google.com
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 3c64ffb..255b3f1 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1346,11 +1346,8 @@
         // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
         // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
         final Task rootTask = task.getRootTask();
-        if (rootTask != null) {
-            DisplayContent display = rootTask.getDisplay();
-            if (display != null && display.isSingleTaskInstance()) {
-                return false;
-            }
+        if (rootTask != null && rootTask.isSingleTaskInstance()) {
+            return false;
         }
 
         // If we're in lock task mode, ignore the root task
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6882dc4..b50cb4c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -814,14 +814,14 @@
     }
 
     boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
-            boolean hasFocus) {
+            boolean focusable) {
         // Update the input consumer touchable region to match the target app main window
         final WindowState targetAppMainWindow = mTargetActivityRecord != null
                 ? mTargetActivityRecord.findMainWindow()
                 : null;
         if (targetAppMainWindow != null) {
             targetAppMainWindow.getBounds(mTmpRect);
-            inputWindowHandle.focusable = hasFocus;
+            inputWindowHandle.focusable = focusable;
             inputWindowHandle.touchableRegion.set(mTmpRect);
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 21e30ce..80ccf3c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -129,6 +129,7 @@
 import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.IntArray;
@@ -162,7 +163,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -214,7 +214,7 @@
     private int mTopFocusedDisplayId = INVALID_DISPLAY;
 
     // Map from the PID to the top most app which has a focused window of the process.
-    final HashMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new HashMap<>();
+    final ArrayMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new ArrayMap<>();
 
     // Only a separate transaction until we separate the apply surface changes
     // transaction from the global transaction.
@@ -480,8 +480,7 @@
             mTopFocusedDisplayId = topFocusedDisplayId;
             mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
             mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
-            ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d",
-                    topFocusedDisplayId);
+            ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId);
         }
         return changed;
     }
@@ -2202,7 +2201,22 @@
         ensureActivitiesVisible(null, 0, false /* preserveWindows */);
         resumeFocusedStacksTopActivities();
 
-        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+        notifyActivityPipModeChanged(r);
+    }
+
+    /**
+     * Notifies when an activity enters or leaves PIP mode.
+     * @param r indicates the activity currently in PIP, can be null to indicate no activity is
+     *          currently in PIP mode.
+     */
+    void notifyActivityPipModeChanged(@Nullable ActivityRecord r) {
+        final boolean inPip = r != null;
+        if (inPip) {
+            mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+        } else {
+            mService.getTaskChangeNotificationController().notifyActivityUnpinned();
+        }
+        mWindowManager.mPolicy.setPipVisibilityLw(inPip);
     }
 
     void executeAppTransitionForAllDisplay() {
@@ -2289,10 +2303,6 @@
 
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            if (display.shouldSleep()) {
-                continue;
-            }
-
             final boolean curResult = result;
             boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas(
                     (taskDisplayArea, resumed) -> {
@@ -2753,7 +2763,7 @@
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         r.detachFromProcess();
-        r.getDisplay().mDisplayContent.prepareAppTransition(
+        r.mDisplayContent.prepareAppTransition(
                 TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
         r.destroyIfPossible("handleAppCrashed");
     }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 33935d6..7df2b40 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -26,6 +26,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.DebugUtils;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
@@ -429,7 +430,8 @@
 
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
-        pw.print(" mAnimationType=" + mAnimationType);
+        pw.print(" mAnimationType=" + DebugUtils.valueToString(SurfaceAnimator.class,
+                "ANIMATION_TYPE_", mAnimationType));
         pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : "");
         pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation);
         if (mAnimation != null) {
@@ -442,56 +444,56 @@
      * No animation is specified.
      * @hide
      */
-    static final int ANIMATION_TYPE_NONE = 0;
+    public static final int ANIMATION_TYPE_NONE = 0;
 
     /**
      * Animation for an app transition.
      * @hide
      */
-    static final int ANIMATION_TYPE_APP_TRANSITION = 1;
+    public static final int ANIMATION_TYPE_APP_TRANSITION = 1;
 
     /**
      * Animation for screen rotation.
      * @hide
      */
-    static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1;
+    public static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1;
 
     /**
      * Animation for dimming.
      * @hide
      */
-    static final int ANIMATION_TYPE_DIMMER = 1 << 2;
+    public static final int ANIMATION_TYPE_DIMMER = 1 << 2;
 
     /**
      * Animation for recent apps.
      * @hide
      */
-    static final int ANIMATION_TYPE_RECENTS = 1 << 3;
+    public static final int ANIMATION_TYPE_RECENTS = 1 << 3;
 
     /**
      * Animation for a {@link WindowState} without animating the activity.
      * @hide
      */
-    static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4;
+    public static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4;
 
     /**
      * Animation to control insets. This is actually not an animation, but is used to give the
      * client a leash over the system window causing insets.
      * @hide
      */
-    static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
+    public static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
 
     /**
      * Animation when a fixed rotation transform is applied to a window token.
      * @hide
      */
-    static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
+    public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
 
     /**
      * Bitmask to include all animation types. This is NOT an {@link AnimationType}
      * @hide
      */
-    static final int ANIMATION_TYPE_ALL = -1;
+    public static final int ANIMATION_TYPE_ALL = -1;
 
     /**
      * The type of the animation.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 19bf451..b32e91f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1434,7 +1434,7 @@
                     && (newParent == null || !newParent.inPinnedWindowingMode())) {
                 // Notify if a task from the pinned stack is being removed
                 // (or moved depending on the mode).
-                mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+                mRootWindowContainer.notifyActivityPipModeChanged(null);
             }
         }
 
@@ -2319,8 +2319,7 @@
             taskDisplayArea.onStackWindowingModeChanged(this);
         }
 
-        final DisplayContent display = getDisplay();
-        if (display == null ) {
+        if (mDisplayContent == null) {
             return;
         }
 
@@ -2336,7 +2335,7 @@
             final int newRotation = getWindowConfiguration().getRotation();
             final boolean rotationChanged = prevRotation != newRotation;
             if (rotationChanged) {
-                display.mDisplayContent.rotateBounds(
+                mDisplayContent.rotateBounds(
                         newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation,
                         newBounds);
                 hasNewOverrideBounds = true;
@@ -2594,15 +2593,12 @@
         outNonDecorBounds.set(bounds);
         outStableBounds.set(bounds);
         final Task rootTask = getRootTask();
-        if (rootTask == null || rootTask.getDisplay() == null) {
-            return;
-        }
-        DisplayPolicy policy = rootTask.getDisplay().mDisplayContent.getDisplayPolicy();
-        if (policy == null) {
+        if (rootTask == null || rootTask.mDisplayContent == null) {
             return;
         }
         mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
 
+        final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
         policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
                 displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
         intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
@@ -2990,14 +2986,6 @@
         }
     }
 
-    @Override
-    DisplayContent getDisplayContent() {
-        // TODO: Why aren't we just using our own display content vs. parent's???
-        final Task stack = getRootTask();
-        return stack != null && stack != this
-                ? stack.getDisplayContent() : super.getDisplayContent();
-    }
-
     int getDisplayId() {
         final DisplayContent dc = getDisplayContent();
         return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
@@ -5114,10 +5102,11 @@
                     : WINDOWING_MODE_FULLSCREEN;
         }
         if (currentMode == WINDOWING_MODE_PINNED) {
-            mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+            mRootWindowContainer.notifyActivityPipModeChanged(null);
         }
         if (likelyResolvedMode == WINDOWING_MODE_PINNED
                 && taskDisplayArea.getRootPinnedTask() != null) {
+
             // Can only have 1 pip at a time, so replace an existing pip
             taskDisplayArea.getRootPinnedTask().dismissPip();
         }
@@ -5173,14 +5162,9 @@
                 !PRESERVE_WINDOWS);
     }
 
-    DisplayContent getDisplay() {
-        return getDisplayContent();
-    }
-
     /** @return true if the stack can only contain one task */
     boolean isSingleTaskInstance() {
-        final DisplayContent display = getDisplay();
-        return display != null && display.isSingleTaskInstance();
+        return mDisplayContent != null && mDisplayContent.isSingleTaskInstance();
     }
 
     final boolean isHomeOrRecentsStack() {
@@ -5445,6 +5429,7 @@
         mAtmService.updateCpuStats();
 
         boolean pauseImmediately = false;
+        boolean shouldAutoPip = false;
         if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
             // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
             // activity to be paused, while at the same time resuming the new resume activity
@@ -5452,26 +5437,39 @@
             // activities a chance to enter Pip before resuming the next activity.
             final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
                     "shouldResumeWhilePausing", userLeaving);
-            if (!lastResumedCanPip) {
+            if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterAllowed()) {
+                shouldAutoPip = true;
+            } else if (!lastResumedCanPip) {
                 pauseImmediately = true;
+            } else {
+                // The previous activity may still enter PIP even though it did not allow auto-PIP.
             }
         }
 
+        boolean didAutoPip = false;
         if (prev.attachedToProcess()) {
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
-            try {
-                EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
-                        prev.shortComponentName, "userLeaving=" + userLeaving);
-
-                mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
-                        prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
-                                prev.configChangeFlags, pauseImmediately));
-            } catch (Exception e) {
-                // Ignore exception, if process died other code will cleanup.
-                Slog.w(TAG, "Exception thrown during pause", e);
+            if (shouldAutoPip) {
+                if (DEBUG_PAUSE) {
+                    Slog.d(TAG_PAUSE, "Auto-PIP allowed, entering PIP mode directly: " + prev);
+                }
+                didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
                 mPausingActivity = null;
-                mLastPausedActivity = null;
-                mLastNoHistoryActivity = null;
+            } else {
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
+                try {
+                    EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+                            prev.shortComponentName, "userLeaving=" + userLeaving);
+
+                    mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+                            prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+                                    prev.configChangeFlags, pauseImmediately));
+                } catch (Exception e) {
+                    // Ignore exception, if process died other code will cleanup.
+                    Slog.w(TAG, "Exception thrown during pause", e);
+                    mPausingActivity = null;
+                    mLastPausedActivity = null;
+                    mLastNoHistoryActivity = null;
+                }
             }
         } else {
             mPausingActivity = null;
@@ -5485,6 +5483,11 @@
             mStackSupervisor.acquireLaunchWakelock();
         }
 
+        if (didAutoPip) {
+            // Already entered PIP mode, no need to keep pausing.
+            return true;
+        }
+
         if (mPausingActivity != null) {
             // Have the window manager pause its key dispatching until the new
             // activity has started.  If we're pausing the activity just because
@@ -5620,8 +5623,7 @@
      * otherwise.
      */
     boolean isFocusedStackOnDisplay() {
-        final DisplayContent display = getDisplay();
-        return display != null && this == display.getFocusedStack();
+        return mDisplayContent != null && this == mDisplayContent.getFocusedStack();
     }
 
     /**
@@ -5760,7 +5762,7 @@
      * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied.
      */
     boolean canShowWithInsecureKeyguard() {
-        final DisplayContent displayContent = getDisplay();
+        final DisplayContent displayContent = mDisplayContent;
         if (displayContent == null) {
             throw new IllegalStateException("Stack is not attached to any display, stackId="
                     + getRootTaskId());
@@ -6348,7 +6350,7 @@
         return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea());
     }
 
-    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+    void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
         Task rTask = r.getTask();
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
@@ -6399,7 +6401,7 @@
         // The transition animation and starting window are not needed if {@code allowMoveToFront}
         // is false, because the activity won't be visible.
         if ((!isHomeOrRecentsStack() || hasActivity()) && allowMoveToFront) {
-            final DisplayContent dc = getDisplay().mDisplayContent;
+            final DisplayContent dc = mDisplayContent;
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare open transition: starting " + r);
             if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
@@ -6410,7 +6412,7 @@
                 if (newTask) {
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
-                    } else if (getDisplay().isSingleTaskInstance()) {
+                    } else if (dc.isSingleTaskInstance()) {
                         // If a new task is being launched in a single task display, we don't need
                         // to play normal animation, but need to trigger a callback when an app
                         // transition is actually handled. So ignore already prepared activity, and
@@ -6454,7 +6456,7 @@
                 ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 // Go ahead to execute app transition for this activity since the app transition
                 // will not be triggered through the resume channel.
-                getDisplay().mDisplayContent.executeAppTransition();
+                mDisplayContent.executeAppTransition();
             } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                 // Figure out if we are transitioning from another activity that is
                 // "has the same starting icon" as the next one.  This allows the
@@ -6567,7 +6569,7 @@
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         Task finishedTask = r.getTask();
-        getDisplay().mDisplayContent.prepareAppTransition(
+        mDisplayContent.prepareAppTransition(
                 TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
         r.finishIfPossible(reason, false /* oomAdj */);
 
@@ -6655,7 +6657,8 @@
         // Basic case: for simple app-centric recents, we need to recreate
         // the task if the affinity has changed.
 
-        final String affinity = ActivityRecord.getTaskAffinityWithUid(destAffinity, srec.getUid());
+        final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid(),
+                srec.launchMode);
         if (srec == null || srec.getTask().affinity == null
                 || !srec.getTask().affinity.equals(affinity)) {
             return true;
@@ -6807,7 +6810,7 @@
                 ActivityOptions.abort(options);
             }
         }
-        getDisplay().mDisplayContent.prepareAppTransition(transit, false,
+        mDisplayContent.prepareAppTransition(transit, false,
                 0 /* flags */, forceOverride);
     }
 
@@ -6855,7 +6858,7 @@
             // Defer updating the IME target since the new IME target will try to get computed
             // before updating all closing and opening apps, which can cause the ime target to
             // get calculated incorrectly.
-            getDisplay().deferUpdateImeTarget();
+            mDisplayContent.deferUpdateImeTarget();
 
             // Shift all activities with this task up to the top
             // of the stack, keeping them in the same internal order.
@@ -6879,7 +6882,7 @@
 
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
             if (noAnimation) {
-                getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_NONE, false);
+                mDisplayContent.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
                 if (r != null) {
                     mStackSupervisor.mNoAnimActivities.add(r);
                 }
@@ -6909,7 +6912,7 @@
             mAtmService.getTaskChangeNotificationController()
                     .notifyTaskMovedToFront(tr.getTaskInfo());
         } finally {
-            getDisplay().continueUpdateImeTarget();
+            mDisplayContent.continueUpdateImeTarget();
         }
     }
 
@@ -6959,7 +6962,7 @@
         if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
                 + tr.mTaskId);
 
-        getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
+        mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false /* alwaysKeepCurrent */);
         moveToBack("moveTaskToBackLocked", tr);
 
         if (inPinnedWindowingMode()) {
@@ -6968,7 +6971,7 @@
         }
 
         mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
-                getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */,
+                mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */,
                 false /* deferResume */);
 
         ActivityRecord topActivity = getDisplayArea().topRunningActivity();
@@ -6976,7 +6979,7 @@
         if (topStack != null && topStack != this && topActivity.isState(RESUMED)) {
             // Usually resuming a top activity triggers the next app transition, but nothing's got
             // resumed in this case, so we need to execute it explicitly.
-            getDisplay().mDisplayContent.executeAppTransition();
+            mDisplayContent.executeAppTransition();
         } else {
             mRootWindowContainer.resumeFocusedStacksTopActivities();
         }
@@ -7575,17 +7578,21 @@
     }
 
     void executeAppTransition(ActivityOptions options) {
-        getDisplay().mDisplayContent.executeAppTransition();
+        mDisplayContent.executeAppTransition();
         ActivityOptions.abort(options);
     }
 
     boolean shouldSleepActivities() {
-        final DisplayContent display = getDisplay();
+        final DisplayContent display = mDisplayContent;
 
         // Do not sleep activities in this stack if we're marked as focused and the keyguard
         // is in the process of going away.
         if (isFocusedStackOnDisplay()
-                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
+                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()
+                // Avoid resuming activities on secondary displays since we don't want bubble
+                // activities to be resumed while bubble is still collapsed.
+                // TODO(b/113840485): Having keyguard going away state for secondary displays.
+                && display.isDefaultDisplay) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index f8465dd..d9290fb 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,6 +23,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
 
@@ -43,16 +44,15 @@
 import android.window.WindowContainerToken;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.function.Consumer;
 
@@ -126,6 +126,7 @@
         }
 
         void onTaskAppeared(Task task) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
             final boolean visible = task.isVisible();
             final RunningTaskInfo taskInfo = task.getTaskInfo();
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
@@ -147,6 +148,7 @@
 
 
         void onTaskVanished(Task task) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId);
             final RunningTaskInfo taskInfo = task.getTaskInfo();
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
                 try {
@@ -163,6 +165,7 @@
                 // by the organizer that don't receive that signal
                 return;
             }
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId);
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
                 if (!task.isOrganized()) {
                     // This is safe to ignore if the task is no longer organized
@@ -177,6 +180,8 @@
         }
 
         void onBackPressedOnTaskRoot(Task task) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task back pressed on root taskId=%d",
+                    task.mTaskId);
             if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) {
                 // Skip if the task has not yet received taskAppeared(), except for tasks created
                 // by the organizer that don't receive that signal
@@ -201,7 +206,6 @@
         private final DeathRecipient mDeathRecipient;
         private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
         private final int mUid;
-        private boolean mInterceptBackPressedOnTaskRoot;
 
         TaskOrganizerState(ITaskOrganizer organizer, int uid) {
             final Consumer<Runnable> deferTaskOrgCallbacksConsumer =
@@ -219,10 +223,6 @@
             mUid = uid;
         }
 
-        void setInterceptBackPressedOnTaskRoot(boolean interceptBackPressed) {
-            mInterceptBackPressedOnTaskRoot = interceptBackPressed;
-        }
-
         void addTask(Task t) {
             if (t.mTaskAppearedSent) return;
 
@@ -242,6 +242,7 @@
                 mOrganizer.onTaskVanished(t);
             }
             mOrganizedTasks.remove(t);
+            mInterceptBackPressedOnRootTasks.remove(t.mTaskId);
         }
 
         void dispose() {
@@ -273,6 +274,8 @@
     private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
     private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
+    // Set of organized tasks (by taskId) that dispatch back pressed to their organizers
+    private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet();
 
     private final ActivityTaskManagerService mService;
 
@@ -306,6 +309,8 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
                 for (int winMode : SUPPORTED_WINDOWING_MODES) {
                     if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
                         mTaskOrganizers.add(organizer);
@@ -327,6 +332,7 @@
     @Override
     public void unregisterTaskOrganizer(ITaskOrganizer organizer) {
         enforceStackPermission("unregisterTaskOrganizer()");
+        final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -334,6 +340,8 @@
                 if (state == null) {
                     return;
                 }
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister task organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
                 state.unlinkDeath();
                 state.dispose();
             }
@@ -383,6 +391,8 @@
                     return null;
                 }
 
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d",
+                        displayId, windowingMode);
                 final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode,
                         ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
                         true /* createdByOrganizer */);
@@ -407,6 +417,9 @@
                     throw new IllegalArgumentException(
                             "Attempt to delete task not created by organizer task=" + task);
                 }
+
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d",
+                        task.getDisplayId(), task.getWindowingMode());
                 task.removeImmediately();
                 return true;
             }
@@ -608,15 +621,23 @@
     }
 
     @Override
-    public void setInterceptBackPressedOnTaskRoot(ITaskOrganizer organizer,
+    public void setInterceptBackPressedOnTaskRoot(WindowContainerToken token,
             boolean interceptBackPressed) {
         enforceStackPermission("setInterceptBackPressedOnTaskRoot()");
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
-                if (state != null) {
-                    state.setInterceptBackPressedOnTaskRoot(interceptBackPressed);
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set intercept back pressed on root=%b",
+                        interceptBackPressed);
+                final Task task = WindowContainer.fromBinder(token.asBinder()).asTask();
+                if (task == null) {
+                    Slog.w(TAG, "Could not resolve task from token");
+                    return;
+                }
+                if (interceptBackPressed) {
+                    mInterceptBackPressedOnRootTasks.add(task.mTaskId);
+                } else {
+                    mInterceptBackPressedOnRootTasks.remove(task.mTaskId);
                 }
             }
         } finally {
@@ -625,15 +646,12 @@
     }
 
     public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
-        if (task == null || !task.isOrganized()) {
+        if (task == null || !task.isOrganized()
+                || !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) {
             return false;
         }
 
         final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder());
-        if (!state.mInterceptBackPressedOnTaskRoot) {
-            return false;
-        }
-
         state.mOrganizer.onBackPressedOnTaskRoot(task);
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e3112ef..f39fa1b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -16,7 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -615,8 +617,8 @@
         return state.calculateInsets(frame, null /* ignoringVisibilityState */,
                 false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
                 null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacyWindowFlags */,
-                0 /* legacySystemUiFlags */, null /* typeSideMap */).getInsets(
-                        WindowInsets.Type.systemBars()).toRect();
+                0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                null /* typeSideMap */).getInsets(WindowInsets.Type.systemBars()).toRect();
     }
 
     void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c45ccb6..3fd8aaf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -376,8 +376,11 @@
     private static final String BOOT_ANIMATION_SERVICE = "bootanim";
 
     static final int UPDATE_FOCUS_NORMAL = 0;
+    /** Caller will assign layers */
     static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
+    /** Caller is performing surface placement */
     static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
+    /** Caller will performSurfacePlacement */
     static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
     /** Indicates we are removing the focused window when updating the focus. */
     static final int UPDATE_FOCUS_REMOVING_FOCUS = 4;
@@ -1407,7 +1410,8 @@
             if (!displayContent.hasAccess(session.mUid)) {
                 ProtoLog.w(WM_ERROR,
                         "Attempted to add window to a display for which the application "
-                                + "does not have access: %d.  Aborting.", displayId);
+                                + "does not have access: %d.  Aborting.",
+                        displayContent.getDisplayId());
                 return WindowManagerGlobal.ADD_INVALID_DISPLAY;
             }
 
@@ -2382,10 +2386,6 @@
             if (win.mActivityRecord != null) {
                 win.mActivityRecord.updateReportedVisibilityLocked();
             }
-            if (winAnimator.mReportSurfaceResized) {
-                winAnimator.mReportSurfaceResized = false;
-                result |= WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED;
-            }
             if (displayPolicy.areSystemBarsForcedShownLw(win)) {
                 result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
             }
@@ -4730,12 +4730,30 @@
         return false;
     }
 
+    void reportFocusChanged(IBinder oldToken, IBinder newToken) {
+        WindowState lastFocus;
+        WindowState newFocus;
+        synchronized (mGlobalLock) {
+            lastFocus = mInputToWindowMap.get(oldToken);
+            newFocus = mInputToWindowMap.get(newToken);
+            ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastFocus, newFocus);
+        }
+
+        if (newFocus != null) {
+            newFocus.reportFocusChangedSerialized(true);
+            notifyFocusChanged();
+        }
+
+        if (lastFocus != null) {
+            lastFocus.reportFocusChangedSerialized(false);
+        }
+    }
+
     // -------------------------------------------------------------
     // Async Handler
     // -------------------------------------------------------------
 
     final class H extends android.os.Handler {
-        public static final int REPORT_FOCUS_CHANGE = 2;
         public static final int WINDOW_FREEZE_TIMEOUT = 11;
 
         public static final int PERSIST_ANIMATION_SCALE = 14;
@@ -4788,50 +4806,6 @@
                 Slog.v(TAG_WM, "handleMessage: entry what=" + msg.what);
             }
             switch (msg.what) {
-                case REPORT_FOCUS_CHANGE: {
-                    final DisplayContent displayContent = (DisplayContent) msg.obj;
-                    WindowState lastFocus;
-                    WindowState newFocus;
-
-                    AccessibilityController accessibilityController = null;
-
-                    synchronized (mGlobalLock) {
-                        if (mAccessibilityController != null) {
-                            accessibilityController = mAccessibilityController;
-                        }
-
-                        lastFocus = displayContent.mLastFocus;
-                        newFocus = displayContent.mCurrentFocus;
-                        if (lastFocus == newFocus) {
-                            // Focus is not changing, so nothing to do.
-                            return;
-                        }
-                        displayContent.mLastFocus = newFocus;
-                        ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus moving from %s"
-                                        + " to %s displayId=%d", lastFocus, newFocus,
-                                displayContent.getDisplayId());
-                    }
-
-                    // First notify the accessibility manager for the change so it has
-                    // the windows before the newly focused one starts firing events.
-                    if (accessibilityController != null) {
-                        accessibilityController.onWindowFocusChangedNotLocked(
-                                displayContent.getDisplayId());
-                    }
-
-                    if (newFocus != null) {
-                        ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus);
-                        newFocus.reportFocusChangedSerialized(true);
-                        notifyFocusChanged();
-                    }
-
-                    if (lastFocus != null) {
-                        ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus);
-                        lastFocus.reportFocusChangedSerialized(false);
-                    }
-                    break;
-                }
-
                 case WINDOW_FREEZE_TIMEOUT: {
                     final DisplayContent displayContent = (DisplayContent) msg.obj;
                     synchronized (mGlobalLock) {
@@ -5775,19 +5749,6 @@
     }
 
     @Override
-    public void setPipVisibility(boolean visible) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Caller does not hold permission "
-                    + android.Manifest.permission.STATUS_BAR);
-        }
-
-        synchronized (mGlobalLock) {
-            mPolicy.setPipVisibilityLw(visible);
-        }
-    }
-
-    @Override
     public void statusBarVisibilityChanged(int displayId, int visibility) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -7994,6 +7955,8 @@
             return;
         }
 
+        ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s",
+                touchedWindow);
         final DisplayContent displayContent = touchedWindow.getDisplayContent();
         if (!displayContent.isOnTop()) {
             displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
@@ -8022,10 +7985,7 @@
             }
         }
 
-        try {
-            mActivityTaskManager.setFocusedTask(task.mTaskId);
-        } catch (RemoteException e) {
-        }
+        mAtmService.setFocusedTask(task.mTaskId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d25a648..c7cad2f 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
@@ -45,6 +46,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 
@@ -127,6 +129,8 @@
                 if (callback != null) {
                     syncId = startSyncWithOrganizer(callback);
                 }
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d",
+                        syncId);
                 mService.deferWindowLayout();
                 try {
                     ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
@@ -427,6 +431,7 @@
 
     @VisibleForTesting
     void setSyncReady(int id) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id);
         mBLASTSyncEngine.setReady(id);
     }
 
@@ -436,9 +441,10 @@
     }
 
     @Override
-    public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
+    public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
         final IWindowContainerTransactionCallback callback =
-                mTransactionCallbacksByPendingSyncId.get(mSyncId);
+                mTransactionCallbacksByPendingSyncId.get(syncId);
 
         SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
         for (WindowContainer container : windowContainersReady) {
@@ -446,14 +452,14 @@
         }
 
         try {
-            callback.onTransactionReady(mSyncId, mergedTransaction);
+            callback.onTransactionReady(syncId, mergedTransaction);
         } catch (RemoteException e) {
             // If there's an exception when trying to send the mergedTransaction to the client, we
             // should immediately apply it here so the transactions aren't lost.
             mergedTransaction.apply();
         }
 
-        mTransactionCallbacksByPendingSyncId.remove(mSyncId);
+        mTransactionCallbacksByPendingSyncId.remove(syncId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index c5ebace..8bf0820 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -729,15 +729,16 @@
             return true;
         }
 
-        final DisplayContent display = activity.getDisplay();
-        if (display == null) {
+        if (!activity.isAttached()) {
             // No need to update if the activity hasn't attach to any display.
             return false;
         }
 
         boolean canUpdate = false;
         final DisplayContent topDisplay =
-                mPreQTopResumedActivity != null ? mPreQTopResumedActivity.getDisplay() : null;
+                (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isAttached())
+                        ? mPreQTopResumedActivity.mDisplayContent
+                        : null;
         // Update the topmost activity if current top activity is
         // - not on any display OR
         // - no longer visible OR
@@ -748,8 +749,9 @@
             canUpdate = true;
         }
 
+        final DisplayContent display = activity.mDisplayContent;
         // Update the topmost activity if the current top activity wasn't on top of the other one.
-        if (!canUpdate && topDisplay.mDisplayContent.compareTo(display.mDisplayContent) < 0) {
+        if (!canUpdate && topDisplay.compareTo(display) < 0) {
             canUpdate = true;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9ff33b1..84a9c75 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2884,12 +2884,25 @@
         return canReceiveKeys(false /* fromUserTouch */);
     }
 
+    public String canReceiveKeysReason(boolean fromUserTouch) {
+        return "fromTouch= " + fromUserTouch
+                + " isVisibleOrAdding=" + isVisibleOrAdding()
+                + " mViewVisibility=" + mViewVisibility
+                + " mRemoveOnExit=" + mRemoveOnExit
+                + " flags=" + mAttrs.flags
+                + " appWindowsAreFocusable="
+                + (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
+                + " canReceiveTouchInput=" + canReceiveTouchInput()
+                + " displayIsOnTop=" + getDisplayContent().isOnTop()
+                + " displayIsTrusted=" + getDisplayContent().isTrusted();
+    }
+
     public boolean canReceiveKeys(boolean fromUserTouch) {
         final boolean canReceiveKeys = isVisibleOrAdding()
                 && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                 && (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
-                && !cantReceiveTouchInput();
+                && canReceiveTouchInput();
         if (!canReceiveKeys) {
             return false;
         }
@@ -2907,15 +2920,18 @@
         return showBecauseOfActivity || showBecauseOfWindow;
     }
 
-    /** @return {@code false} if this window desires touch events. */
-    boolean cantReceiveTouchInput() {
-        if (mActivityRecord == null || mActivityRecord.getTask() == null) {
-            return false;
+    /**
+     * @return {@code true} if this window can receive touches based on among other things,
+     * windowing state and recents animation state.
+     **/
+    boolean canReceiveTouchInput() {
+        if (mActivityRecord == null  || mActivityRecord.getTask() == null) {
+            return true;
         }
 
-        return mActivityRecord.getTask().getRootTask().shouldIgnoreInput()
-                || !mActivityRecord.mVisibleRequested
-                || isRecentsAnimationConsumingAppInput();
+        return !mActivityRecord.getTask().getRootTask().shouldIgnoreInput()
+                && mActivityRecord.mVisibleRequested
+                && !isRecentsAnimationConsumingAppInput();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1bd712c..5e81400 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -126,11 +126,7 @@
      * we must tell them application to resize (and thus redraw itself).
      */
     boolean mSurfaceResized;
-    /**
-     * Whether we should inform the client on next relayoutWindow that
-     * the surface has been resized since last time.
-     */
-    boolean mReportSurfaceResized;
+
     WindowSurfaceController mSurfaceController;
     private WindowSurfaceController mPendingDestroySurface;
 
@@ -872,7 +868,6 @@
         }
 
         if (mSurfaceResized) {
-            mReportSurfaceResized = true;
             mWin.getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
         }
     }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4b5f38c..d84f9d1 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -97,6 +97,7 @@
         "libnativehelper",
         "libnativewindow",
         "libpowermanager",
+        "libprocessgroup",
         "libutils",
         "libui",
         "libvibratorservice",
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 6a6da0e..7e9e11d 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -30,6 +30,7 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <jni.h>
+#include <processgroup/processgroup.h>
 
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -74,9 +75,26 @@
     }
 }
 
+static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal(
+        JNIEnv *env, jobject clazz, jboolean enable) {
+    bool success = true;
+
+    if (enable) {
+        success = SetTaskProfiles(0, {"FreezerEnabled"}, true);
+    } else {
+        success = SetTaskProfiles(0, {"FreezerDisabled"}, true);
+    }
+
+    if (!success) {
+        jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+    }
+}
+
 static const JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
     {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
+    {"enableFreezerInternal", "(Z)V",
+        (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
 };
 
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9751c46..5dd6cd7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -233,7 +233,8 @@
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
     virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
+    virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier& identifier);
     virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
     virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
             jfloatArray matrixArr);
@@ -622,12 +623,12 @@
     checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged");
 }
 
-sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
+std::shared_ptr<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
         const InputDeviceIdentifier& identifier) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
 
-    sp<KeyCharacterMap> result;
+    std::shared_ptr<KeyCharacterMap> result;
     ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.c_str()));
     ScopedLocalRef<jobject> identifierObj(env, env->NewObject(gInputDeviceIdentifierInfo.clazz,
             gInputDeviceIdentifierInfo.constructor, descriptor.get(),
@@ -642,8 +643,12 @@
         ScopedUtfChars filenameChars(env, filenameObj.get());
         ScopedUtfChars contentsChars(env, contentsObj.get());
 
-        KeyCharacterMap::loadContents(filenameChars.c_str(),
-                contentsChars.c_str(), KeyCharacterMap::FORMAT_OVERLAY, &result);
+        base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+                KeyCharacterMap::loadContents(filenameChars.c_str(), contentsChars.c_str(),
+                                              KeyCharacterMap::FORMAT_OVERLAY);
+        if (ret) {
+            result = *ret;
+        }
     }
     checkAndClearExceptionFromCallback(env, "getKeyboardLayoutOverlay");
     return result;
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 777cbf4..4e53aa2 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -928,7 +928,7 @@
  * GnssPsdsCallback class implements the callback methods for the IGnssPsds
  * interface.
  */
-class GnssPsdsCallback : public IGnssPsdsCallback {
+struct GnssPsdsCallback : public IGnssPsdsCallback {
     Return<void> downloadRequestCb() override;
     Return<void> downloadRequestCb_3_0(int32_t psdsType) override;
 };
@@ -2743,19 +2743,26 @@
 
 static jboolean android_location_GnssLocationProvider_supports_psds(
         JNIEnv* /* env */, jobject /* obj */) {
-    return (gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+    return (gnssPsdsIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE;
 }
 
 static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jobject /* obj */,
-        jbyteArray data, jint length) {
-    if (gnssXtraIface == nullptr) {
-        ALOGE("%s: IGnssXtra interface not available.", __func__);
+                                                                   jbyteArray data, jint length,
+                                                                   jint psdsType) {
+    if (gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
+        ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__);
         return;
     }
 
     jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
-    auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length));
-    checkHidlReturn(result, "IGnssXtra injectXtraData() failed.");
+    if (gnssPsdsIface != nullptr) {
+        auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType,
+                                                        std::string((const char*)bytes, length));
+        checkHidlReturn(result, "IGnssPsds injectPsdsData() failed.");
+    } else if (gnssXtraIface != nullptr) {
+        auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length));
+        checkHidlReturn(result, "IGnssXtra injectXtraData() failed.");
+    }
     env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
 }
 
@@ -3685,7 +3692,7 @@
          reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_location)},
         {"native_supports_psds", "()Z",
          reinterpret_cast<void*>(android_location_GnssLocationProvider_supports_psds)},
-        {"native_inject_psds_data", "([BI)V",
+        {"native_inject_psds_data", "([BII)V",
          reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_psds_data)},
         {"native_agps_set_id", "(ILjava/lang/String;)V",
          reinterpret_cast<void*>(android_location_GnssLocationProvider_agps_set_id)},
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 0277f16..46e6f91 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -33,6 +33,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <type_traits>
+
 namespace android {
 
 namespace {
@@ -53,7 +55,7 @@
 
     fsverity_enable_arg arg = {};
     arg.version = 1;
-    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; // hardcoded in measureFsverity below
     arg.block_size = 4096;
     arg.salt_size = 0;
     arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
@@ -85,9 +87,41 @@
     return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
 }
 
+int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) {
+    static constexpr auto kDigestSha256 = 32;
+    using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kDigestSha256>;
+
+    Storage bytes;
+    fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
+    data->digest_size = kDigestSha256; // the only input/output parameter
+
+    ScopedUtfChars path(env, filePath);
+    ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+    if (rfd.get() < 0) {
+        return rfd.get();
+    }
+    if (auto err = ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data); err < 0) {
+        return err;
+    }
+
+    if (data->digest_algorithm != FS_VERITY_HASH_ALG_SHA256) {
+        return -EINVAL;
+    }
+
+    if (digest != nullptr && data->digest_size > 0) {
+        auto digestSize = env->GetArrayLength(digest);
+        if (data->digest_size > digestSize) {
+            return -E2BIG;
+        }
+        env->SetByteArrayRegion(digest, 0, data->digest_size, (const jbyte *)data->digest);
+    }
+
+    return 0;
+}
 const JNINativeMethod sMethods[] = {
         {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity},
         {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
+        {"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity},
 };
 
 }  // namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 22e309c..b6f0f9f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5782,9 +5782,6 @@
         if (!mHasFeature) {
             return;
         }
-        final CallerIdentity identity = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isSystemUid(identity) || isRootUid(identity)
-                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL));
 
         final ActiveAdmin admin;
         synchronized (getLockObject()) {
@@ -7135,9 +7132,22 @@
         return (deviceOwner != null) ? deviceOwner.keepUninstalledPackages : null;
     }
 
+    /**
+     * Logs a warning when the device doesn't have {@code PackageManager.FEATURE_DEVICE_ADMIN}.
+     *
+     * @param message action that was not executed; should not end with a period because the missing
+     * feature will be appended to it.
+     */
+    private void logMissingFeatureAction(String message) {
+        Slog.w(LOG_TAG, message + " because device does not have the "
+                + PackageManager.FEATURE_DEVICE_ADMIN + " feature.");
+    }
+
     @Override
     public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
         if (!mHasFeature) {
+            logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(admin)
+                    + " as device owner for user " + userId);
             return false;
         }
         if (admin == null
@@ -7459,6 +7469,8 @@
     @Override
     public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
         if (!mHasFeature) {
+            logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(who)
+                    + " as profile owner for user " + userHandle);
             return false;
         }
         if (who == null
@@ -7679,6 +7691,8 @@
     @Override
     public void setUserProvisioningState(int newState, int userHandle) {
         if (!mHasFeature) {
+            logMissingFeatureAction("Cannot set provisioning state " + newState + " for user "
+                    + userHandle);
             return;
         }
 
@@ -7756,6 +7770,8 @@
     @Override
     public void setProfileEnabled(ComponentName who) {
         if (!mHasFeature) {
+            logMissingFeatureAction("Cannot enable profile for "
+                    + ComponentName.flattenToShortString(who));
             return;
         }
         Objects.requireNonNull(who, "ComponentName is null");
@@ -9438,8 +9454,7 @@
         Preconditions.checkCallAuthorization(isDeviceOwner(identity));
 
         return mInjector.binderWithCleanCallingIdentity(() -> {
-            final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true
-                    /*excludeDying*/);
+            final List<UserInfo> userInfos = mInjector.getUserManager().getAliveUsers();
             final List<UserHandle> userHandles = new ArrayList<>();
             for (UserInfo userInfo : userInfos) {
                 UserHandle userHandle = userInfo.getUserHandle();
@@ -10362,7 +10377,7 @@
 
     private void maybeClearLockTaskPolicyLocked() {
         mInjector.binderWithCleanCallingIdentity(() -> {
-            final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true);
+            final List<UserInfo> userInfos = mUserManager.getAliveUsers();
             for (int i = userInfos.size() - 1; i >= 0; i--) {
                 int userId = userInfos.get(i).id;
                 if (canUserUseLockTaskLocked(userId)) {
@@ -10849,7 +10864,7 @@
      * them.
      */
     void updateUserSetupCompleteAndPaired() {
-        List<UserInfo> users = mUserManager.getUsers(true);
+        List<UserInfo> users = mUserManager.getAliveUsers();
         final int N = users.size();
         for (int i = 0; i < N; i++) {
             int userHandle = users.get(i).id;
@@ -12052,14 +12067,6 @@
     }
 
     @Override
-    public boolean isSystemOnlyUser(ComponentName admin) {
-        Objects.requireNonNull(admin, "ComponentName is null");
-        final CallerIdentity identity = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(isDeviceOwner(identity));
-        return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM;
-    }
-
-    @Override
     public void reboot(ComponentName admin) {
         Objects.requireNonNull(admin, "ComponentName is null");
         final CallerIdentity identity = getCallerIdentity(admin);
@@ -12579,7 +12586,7 @@
 
     private boolean areAllUsersAffiliatedWithDeviceLocked() {
         return mInjector.binderWithCleanCallingIdentity(() -> {
-            final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true);
+            final List<UserInfo> userInfos = mUserManager.getAliveUsers();
             for (int i = 0; i < userInfos.size(); i++) {
                 int userId = userInfos.get(i).id;
                 if (!isUserAffiliatedWithDeviceLocked(userId)) {
@@ -13048,7 +13055,7 @@
                     }
                 } else {
                     // Caller is the device owner: Look for profile owners that it can bind to.
-                    final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true);
+                    final List<UserInfo> userInfos = mUserManager.getAliveUsers();
                     for (int i = 0; i < userInfos.size(); i++) {
                         final int userId = userInfos.get(i).id;
                         if (userId != callingUserId && canUserBindToDeviceOwnerLocked(userId)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 3cdd482..7649af4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -169,7 +169,7 @@
             // First, try to read from the legacy file.
             final File legacy = getLegacyConfigFile();
 
-            final List<UserInfo> users = mUserManager.getUsers(true);
+            final List<UserInfo> users = mUserManager.getAliveUsers();
 
             if (readLegacyOwnerFileLocked(legacy)) {
                 if (DEBUG) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 7534c7c..e978ed4 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -51,9 +51,9 @@
     static_libs: [
         "libbase",
         "libext2_uuid",
-        "libdataloader_aidl-cpp",
-        "libincremental_aidl-cpp",
-        "libincremental_manager_aidl-cpp",
+        "libdataloader_aidl-unstable-cpp",
+        "libincremental_aidl-unstable-cpp",
+        "libincremental_manager_aidl-unstable-cpp",
         "libprotobuf-cpp-lite",
         "service.incremental.proto",
         "libutils",
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 41945a2..87ae4d7 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -237,6 +237,13 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId,
+                                                           const std::string& path,
+                                                           int32_t* _aidl_return) {
+    *_aidl_return = mImpl.isFileFullyLoaded(storageId, path);
+    return ok();
+}
+
 binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId,
                                                             float* _aidl_return) {
     *_aidl_return = mImpl.getLoadingProgress(storageId);
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 8b40350..8478142 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -66,6 +66,8 @@
                             int32_t destStorageId, const std::string& destPath,
                             int32_t* _aidl_return) final;
     binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final;
+    binder::Status isFileFullyLoaded(int32_t storageId, const std::string& path,
+                                     int32_t* _aidl_return) final;
     binder::Status getLoadingProgress(int32_t storageId, float* _aidl_return) final;
     binder::Status getMetadataByPath(int32_t storageId, const std::string& path,
                                      std::vector<uint8_t>* _aidl_return) final;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index ba6ae92..447ee55 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1603,7 +1603,8 @@
 
     const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
     if (!writeFd.ok()) {
-        LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd;
+        LOG(ERROR) << "Failed to open write fd for: " << targetLibPath
+                   << " errno: " << writeFd.get();
         return;
     }
 
@@ -1673,6 +1674,37 @@
     return mRunning;
 }
 
+int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const {
+    std::unique_lock l(mLock);
+    const auto ifs = getIfsLocked(storage);
+    if (!ifs) {
+        LOG(ERROR) << "isFileFullyLoaded failed, invalid storageId: " << storage;
+        return -EINVAL;
+    }
+    const auto storageInfo = ifs->storages.find(storage);
+    if (storageInfo == ifs->storages.end()) {
+        LOG(ERROR) << "isFileFullyLoaded failed, no storage: " << storage;
+        return -EINVAL;
+    }
+    l.unlock();
+    return isFileFullyLoadedFromPath(*ifs, path);
+}
+
+int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs,
+                                                  std::string_view filePath) const {
+    const auto [filledBlocks, totalBlocks] = mIncFs->countFilledBlocks(ifs.control, filePath);
+    if (filledBlocks < 0) {
+        LOG(ERROR) << "isFileFullyLoadedFromPath failed to get filled blocks count for: "
+                   << filePath << " errno: " << filledBlocks;
+        return filledBlocks;
+    }
+    if (totalBlocks < filledBlocks) {
+        LOG(ERROR) << "isFileFullyLoadedFromPath failed to get total num of blocks";
+        return -EINVAL;
+    }
+    return totalBlocks - filledBlocks;
+}
+
 float IncrementalService::getLoadingProgress(StorageId storage) const {
     std::unique_lock l(mLock);
     const auto ifs = getIfsLocked(storage);
@@ -1706,8 +1738,8 @@
     }
 
     if (totalBlocks == 0) {
-        LOG(ERROR) << "getLoadingProgress failed to get total num of blocks";
-        return -EINVAL;
+        // No file in the storage or files are empty; regarded as fully loaded
+        return 1;
     }
     return (float)filledBlocks / (float)totalBlocks;
 }
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index cd6bfed..267458d 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -132,6 +132,7 @@
              std::string_view newPath);
     int unlink(StorageId storage, std::string_view path);
 
+    int isFileFullyLoaded(StorageId storage, const std::string& path) const;
     float getLoadingProgress(StorageId storage) const;
 
     RawMetadata getMetadata(StorageId storage, std::string_view path) const;
@@ -339,6 +340,7 @@
     int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
     binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
 
+    int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
     float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
 
     void registerAppOpsCallback(const std::string& packageName);
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 1ed46c4..f6d89c5 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -195,8 +195,8 @@
     ErrorCode unlink(const Control& control, std::string_view path) const final {
         return incfs::unlink(control, path);
     }
-    base::unique_fd openForSpecialOps(const Control& control, FileId id) const final {
-        return base::unique_fd{incfs::openForSpecialOps(control, id).release()};
+    incfs::UniqueFd openForSpecialOps(const Control& control, FileId id) const final {
+        return incfs::openForSpecialOps(control, id);
     }
     ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final {
         return incfs::writeBlocks({blocks.data(), size_t(blocks.size())});
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 82a1704..6376d86 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -74,6 +74,7 @@
     using Control = incfs::Control;
     using FileId = incfs::FileId;
     using ErrorCode = incfs::ErrorCode;
+    using UniqueFd = incfs::UniqueFd;
     using WaitResult = incfs::WaitResult;
 
     using ExistingMountCallback =
@@ -96,7 +97,7 @@
     virtual ErrorCode link(const Control& control, std::string_view from,
                            std::string_view to) const = 0;
     virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
-    virtual base::unique_fd openForSpecialOps(const Control& control, FileId id) const = 0;
+    virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0;
     virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0;
     virtual WaitResult waitForPendingReads(
             const Control& control, std::chrono::milliseconds timeout,
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 44cef49..a290a17 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -289,7 +289,7 @@
                        ErrorCode(const Control& control, std::string_view from,
                                  std::string_view to));
     MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
-    MOCK_CONST_METHOD2(openForSpecialOps, base::unique_fd(const Control& control, FileId id));
+    MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id));
     MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
     MOCK_CONST_METHOD3(waitForPendingReads,
                        WaitResult(const Control& control, std::chrono::milliseconds timeout,
@@ -304,6 +304,10 @@
         ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(1, 2)));
     }
 
+    void countFilledBlocksFullyLoaded() {
+        ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(10000, 10000)));
+    }
+
     void countFilledBlocksFails() {
         ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(-1, -1)));
     }
@@ -1069,7 +1073,54 @@
     ASSERT_EQ(res, 0);
 }
 
-TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithNoFile) {
+TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) {
+    mIncFs->countFilledBlocksFails();
+    mFs->hasNoFile();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
+}
+
+TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) {
+    mIncFs->countFilledBlocksFails();
+    mFs->hasFiles();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
+    ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
+}
+
+TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) {
+    mIncFs->countFilledBlocksEmpty();
+    mFs->hasFiles();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
+    ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
+}
+
+TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) {
+    mIncFs->countFilledBlocksFullyLoaded();
+    mFs->hasFiles();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
+    ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
+}
+
+TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) {
     mIncFs->countFilledBlocksSuccess();
     mFs->hasNoFile();
 
@@ -1077,7 +1128,7 @@
     int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
                                                        IncrementalService::CreateOptions::CreateNew,
                                                        {}, {}, {});
-    ASSERT_EQ(-EINVAL, mIncrementalService->getLoadingProgress(storageId));
+    ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
 }
 
 TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
@@ -1092,7 +1143,7 @@
     ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId));
 }
 
-TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithEmptyRanges) {
+TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
     mIncFs->countFilledBlocksEmpty();
     mFs->hasFiles();
 
@@ -1101,7 +1152,7 @@
                                                        IncrementalService::CreateOptions::CreateNew,
                                                        {}, {}, {});
     EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
-    ASSERT_EQ(-EINVAL, mIncrementalService->getLoadingProgress(storageId));
+    ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
 }
 
 TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 12c69ea..ddd1f75 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,11 @@
 
 package com.android.server.profcollect;
 
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
@@ -34,6 +39,7 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
 
 /**
  * System-server-local proxy into the {@code IProfcollectd} native service.
@@ -41,28 +47,43 @@
 public final class ProfcollectForwardingService extends SystemService {
     public static final String LOG_TAG = "ProfcollectForwardingService";
 
+    private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
+
+    private static final long BG_PROCESS_PERIOD = DEBUG
+            ? TimeUnit.MINUTES.toMillis(1)
+            : TimeUnit.DAYS.toMillis(1);
+
     private IProfCollectd mIProfcollect;
-    private ProfcollectForwardingService mSelfService;
+    private static ProfcollectForwardingService sSelfService;
     private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
 
     public ProfcollectForwardingService(Context context) {
         super(context);
 
-        if (mSelfService != null) {
+        if (sSelfService != null) {
             throw new AssertionError("only one service instance allowed");
         }
-        mSelfService = this;
+        sSelfService = this;
     }
 
     @Override
     public void onStart() {
-        Log.i(LOG_TAG, "Profcollect forwarding service start");
-        connectNativeService();
-        if (mIProfcollect == null) {
-            return;
+        if (DEBUG) {
+            Log.d(LOG_TAG, "Profcollect forwarding service start");
         }
-        if (serviceHasSupportedTraceProvider()) {
-            registerObservers();
+        connectNativeService();
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_BOOT_COMPLETED) {
+            if (mIProfcollect == null) {
+                return;
+            }
+            if (serviceHasSupportedTraceProvider()) {
+                registerObservers();
+            }
+            ProfcollectBGJobService.schedule(getContext());
         }
     }
 
@@ -130,6 +151,50 @@
         }
     }
 
+    /**
+     * Background trace process service.
+     */
+    public static class ProfcollectBGJobService extends JobService {
+        // Unique ID in system service
+        private static final int JOB_IDLE_PROCESS = 260817;
+        private static final ComponentName JOB_SERVICE_NAME = new ComponentName(
+                "android",
+                ProfcollectBGJobService.class.getName());
+
+        /**
+         * Attach the service to the system job scheduler.
+         */
+        public static void schedule(Context context) {
+            JobScheduler js = context.getSystemService(JobScheduler.class);
+
+            js.schedule(new JobInfo.Builder(JOB_IDLE_PROCESS, JOB_SERVICE_NAME)
+                    .setRequiresDeviceIdle(true)
+                    .setRequiresCharging(true)
+                    .setPeriodic(BG_PROCESS_PERIOD)
+                    .build());
+        }
+
+        @Override
+        public boolean onStartJob(JobParameters params) {
+            if (DEBUG) {
+                Log.d(LOG_TAG, "Starting background process job");
+            }
+
+            try {
+                sSelfService.mIProfcollect.ProcessProfile();
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, e.getMessage());
+            }
+            return true;
+        }
+
+        @Override
+        public boolean onStopJob(JobParameters params) {
+            // TODO: Handle this?
+            return false;
+        }
+    }
+
     // Event observers
     private void registerObservers() {
         registerAppLaunchObserver();
@@ -155,7 +220,9 @@
         int randomNum = ThreadLocalRandom.current().nextInt(100);
         if (randomNum < traceFrequency) {
             try {
-                Log.i(LOG_TAG, "Tracing on app launch event: " + packageName);
+                if (DEBUG) {
+                    Log.d(LOG_TAG, "Tracing on app launch event: " + packageName);
+                }
                 mIProfcollect.TraceOnce("applaunch");
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, e.getMessage());
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 6cd083e..f4c6918 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -67,7 +67,6 @@
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
 import android.app.IActivityManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.Sensor;
@@ -87,6 +86,7 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.SystemClock;
+import android.provider.DeviceConfig;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -99,6 +99,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
@@ -106,6 +107,8 @@
 import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
+import java.util.concurrent.Executor;
+
 /**
  * Tests for {@link com.android.server.DeviceIdleController}.
  */
@@ -124,8 +127,6 @@
     @Mock
     private ConnectivityManager mConnectivityManager;
     @Mock
-    private ContentResolver mContentResolver;
-    @Mock
     private IActivityManager mIActivityManager;
     @Mock
     private LocationManager mLocationManager;
@@ -294,6 +295,7 @@
         mMockingSession = mockitoSession()
                 .initMocks(this)
                 .strictness(Strictness.LENIENT)
+                .spyStatic(DeviceConfig.class)
                 .spyStatic(LocalServices.class)
                 .startMocking();
         spyOn(getContext());
@@ -310,6 +312,14 @@
                 .thenReturn(mock(PowerSaveState.class));
         doReturn(mock(NetworkPolicyManagerInternal.class))
                 .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class));
+        doAnswer((Answer<Void>) invocationOnMock -> null)
+                .when(() -> DeviceConfig.addOnPropertiesChangedListener(
+                        anyString(), any(Executor.class),
+                        any(DeviceConfig.OnPropertiesChangedListener.class)));
+        doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock
+                -> mock(DeviceConfig.Properties.class))
+                .when(() -> DeviceConfig.getProperties(
+                        anyString(), ArgumentMatchers.<String>any()));
         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
         doNothing().when(mWakeLock).acquire();
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
@@ -319,7 +329,6 @@
         mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
         mAnyMotionDetector = new AnyMotionDetectorForTest();
         mInjector = new InjectorForTest(getContext());
-        doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
 
         mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
         spyOn(mDeviceIdleController);
@@ -330,8 +339,7 @@
         mDeviceIdleController.setLightEnabledForTest(true);
 
         // Get the same Constants object that mDeviceIdleController got.
-        mConstants = mInjector.getConstants(mDeviceIdleController,
-                mInjector.getHandler(mDeviceIdleController), mContentResolver);
+        mConstants = mInjector.getConstants(mDeviceIdleController);
     }
 
     @After
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 77232dc..0789d68 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -780,7 +780,7 @@
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, alarmPi);
         assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed());
 
-        final SparseArray<ArrayList<AlarmManagerService.Alarm>> restrictedAlarms =
+        final SparseArray<ArrayList<Alarm>> restrictedAlarms =
                 mService.mPendingBackgroundAlarms;
         assertNull(restrictedAlarms.get(TEST_CALLING_UID));
 
@@ -993,7 +993,7 @@
 
     @Test
     public void alarmCountOnRemoveFromPendingWhileIdle() {
-        mService.mPendingIdleUntil = mock(AlarmManagerService.Alarm.class);
+        mService.mPendingIdleUntil = mock(Alarm.class);
         final int numAlarms = 15;
         final PendingIntent[] pis = new PendingIntent[numAlarms];
         for (int i = 0; i < numAlarms; i++) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
new file mode 100644
index 0000000..9e43b4a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 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.alarm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AlarmStoreTest {
+    private static final int TEST_CALLING_UID = 12345;
+    private static final String TEST_CALLING_PACKAGE = "android.alarm.unit.test";
+
+    private AlarmStore mAlarmStore;
+
+    @Before
+    public void setUp() {
+        mAlarmStore = new BatchingAlarmStore(null);
+    }
+
+    private static Alarm createAlarm(long whenElapsed, long windowLength, PendingIntent mockPi,
+            AlarmManager.AlarmClockInfo alarmClock) {
+        return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, mockPi,
+                alarmClock);
+    }
+
+    private static Alarm createWakeupAlarm(long whenElapsed, long windowLength,
+            PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) {
+        return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, mockPi,
+                alarmClock);
+    }
+
+    private static Alarm createAlarm(int type, long whenElapsed, long windowLength,
+            PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) {
+        return new Alarm(type, whenElapsed, whenElapsed, windowLength, whenElapsed + windowLength,
+                0, mockPi, null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+    }
+
+    private void addAlarmsToStore(Alarm... alarms) {
+        for (Alarm a : alarms) {
+            mAlarmStore.add(a);
+        }
+    }
+
+    @Test
+    public void add() {
+        final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null);
+        mAlarmStore.add(a1);
+        assertEquals(1, mAlarmStore.size());
+
+        final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null);
+        mAlarmStore.add(a2);
+        assertEquals(2, mAlarmStore.size());
+
+        ArrayList<Alarm> alarmsAdded = mAlarmStore.asList();
+        assertEquals(2, alarmsAdded.size());
+        assertTrue(alarmsAdded.contains(a1) && alarmsAdded.contains(a2));
+    }
+
+    @Test
+    public void remove() {
+        final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null);
+        final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null);
+        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
+        addAlarmsToStore(a1, a2, a5);
+
+        ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.whenElapsed < 4));
+        assertEquals(2, removed.size());
+        assertEquals(1, mAlarmStore.size());
+        assertTrue(removed.contains(a1) && removed.contains(a2));
+
+        final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null);
+        addAlarmsToStore(a8, a2, a1);
+
+        removed = mAlarmStore.remove(unused -> false);
+        assertEquals(0, removed.size());
+        assertEquals(4, mAlarmStore.size());
+
+        removed = mAlarmStore.remove(unused -> true);
+        assertEquals(4, removed.size());
+        assertEquals(0, mAlarmStore.size());
+    }
+
+    @Test
+    public void removePendingAlarms() {
+        final Alarm a1_11 = createAlarm(1, 10, mock(PendingIntent.class), null);
+        final Alarm a2_5 = createAlarm(2, 3, mock(PendingIntent.class), null);
+        final Alarm a6_9 = createAlarm(6, 3, mock(PendingIntent.class), null);
+        addAlarmsToStore(a2_5, a6_9, a1_11);
+
+        final ArrayList<Alarm> pendingAt0 = mAlarmStore.removePendingAlarms(0);
+        assertEquals(0, pendingAt0.size());
+        assertEquals(3, mAlarmStore.size());
+
+        final ArrayList<Alarm> pendingAt3 = mAlarmStore.removePendingAlarms(3);
+        assertEquals(2, pendingAt3.size());
+        assertTrue(pendingAt3.contains(a1_11) && pendingAt3.contains(a2_5));
+        assertEquals(1, mAlarmStore.size());
+
+        addAlarmsToStore(a2_5, a1_11);
+        final ArrayList<Alarm> pendingAt7 = mAlarmStore.removePendingAlarms(7);
+        assertEquals(3, pendingAt7.size());
+        assertTrue(pendingAt7.contains(a1_11) && pendingAt7.contains(a2_5) && pendingAt7.contains(
+                a6_9));
+        assertEquals(0, mAlarmStore.size());
+    }
+
+    @Test
+    public void getNextWakeupDeliveryTime() {
+        final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null);
+        final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null);
+        final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null);
+        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
+        addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10);
+
+        // The wakeup alarms are [6] and [3, 8], hence 6 is the latest time till when we can
+        // defer delivering any wakeup alarm.
+        assertTrue(mAlarmStore.getNextWakeupDeliveryTime() <= 6);
+
+        mAlarmStore.remove(a -> a.wakeup);
+        assertEquals(2, mAlarmStore.size());
+        // No wakeup alarms left.
+        assertEquals(0, mAlarmStore.getNextWakeupDeliveryTime());
+
+        mAlarmStore.remove(unused -> true);
+        assertEquals(0, mAlarmStore.getNextWakeupDeliveryTime());
+    }
+
+    @Test
+    public void getNextDeliveryTime() {
+        final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null);
+        final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null);
+        final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null);
+        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
+        addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10);
+
+        assertTrue(mAlarmStore.getNextDeliveryTime() <= 5);
+
+        mAlarmStore.remove(unused -> true);
+        assertEquals(0, mAlarmStore.getNextWakeupDeliveryTime());
+    }
+
+    @Test
+    public void recalculateAlarmDeliveries() {
+        final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
+        final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null);
+        final Alarm a10 = createAlarm(10, 0, mock(PendingIntent.class), null);
+        addAlarmsToStore(a8, a10, a5);
+
+        assertEquals(5, mAlarmStore.getNextDeliveryTime());
+
+        mAlarmStore.recalculateAlarmDeliveries(a -> {
+            a.whenElapsed += 3;
+            a.maxWhenElapsed = a.whenElapsed;
+            return true;
+        });
+        assertEquals(8, mAlarmStore.getNextDeliveryTime());
+
+        mAlarmStore.recalculateAlarmDeliveries(a -> {
+            a.whenElapsed = 20 - a.whenElapsed;
+            a.maxWhenElapsed = a.whenElapsed;
+            return true;
+        });
+        assertEquals(7, mAlarmStore.getNextDeliveryTime());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
index 12e953a..6465739 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
@@ -27,7 +27,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.ObjectUtils;
-import com.android.server.alarm.AlarmManagerService.Alarm;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index fdcadf3..80ad0a8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -33,6 +33,7 @@
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
 import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.LocationUtils.createLocation;
+import static com.android.server.location.listeners.RemoteListenerRegistration.IN_PROCESS_EXECUTOR;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -85,7 +86,6 @@
 import com.android.internal.location.ProviderRequest;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.location.listeners.ListenerRegistration;
 import com.android.server.location.util.FakeUserInfoHelper;
 import com.android.server.location.util.TestInjector;
 
@@ -274,60 +274,55 @@
 
     @Test
     public void testGetLastLocation_Fine() {
-        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
     }
 
     @Test
     public void testGetLastLocation_Coarse() {
-        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        Location coarse = mManager.getLastLocation(request, IDENTITY, PERMISSION_COARSE);
+        Location coarse = mManager.getLastLocation(IDENTITY, PERMISSION_COARSE, false);
         assertThat(coarse).isNotEqualTo(loc);
         assertThat(coarse).isNearby(loc, 5000);
     }
 
     @Test
     public void testGetLastLocation_Bypass() {
-        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
-        LocationRequest bypassRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0,
-                false).setLocationSettingsIgnored(true);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
-        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isNull();
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
-        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
                 loc);
 
         mProvider.setProviderAllowed(false);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
-        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
                 loc);
 
         loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
-        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
                 loc);
 
         mProvider.setProviderAllowed(true);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
-        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
                 loc);
 
         loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
-        assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo(
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo(
                 loc);
     }
 
@@ -337,13 +332,12 @@
         mockProvider.setAllowed(true);
         mManager.setMockProvider(mockProvider);
 
-        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
         Location loc = createLocation(NAME, mRandom);
         mockProvider.setProviderLocation(loc);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
 
         mManager.setMockProvider(null);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull();
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull();
     }
 
     @Test
@@ -351,13 +345,12 @@
         Location loc1 = createLocation(NAME, mRandom);
         mManager.injectLastLocation(loc1, CURRENT_USER);
 
-        LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false);
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1);
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1);
 
         Location loc2 = createLocation(NAME, mRandom);
         mManager.injectLastLocation(loc2, CURRENT_USER);
 
-        assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1);
+        assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1);
     }
 
     @Test
@@ -381,9 +374,7 @@
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
 
-        LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0,
-                0, false);
-        assertThat(mPassive.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc);
+        assertThat(mPassive.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc);
     }
 
     @Test
@@ -484,7 +475,7 @@
                 PERMISSION_FINE, listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
-        ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+        IN_PROCESS_EXECUTOR.execute(() -> {
             try {
                 blocker.await();
             } catch (InterruptedException e) {
@@ -622,7 +613,7 @@
                 PERMISSION_FINE, listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
-        ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+        IN_PROCESS_EXECUTOR.execute(() -> {
             try {
                 blocker.await();
             } catch (InterruptedException e) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
index 1ef1255..69a9f44 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.location.listeners;
 
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -27,8 +29,6 @@
 import static org.mockito.Mockito.verify;
 import static org.testng.Assert.assertThrows;
 
-import android.location.util.identity.CallerIdentity;
-import android.os.Process;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -324,10 +324,8 @@
         boolean mActive = true;
 
         protected TestListenerRegistration(Integer integer,
-                Consumer<TestListenerRegistration> consumer,
-                boolean outOfProcess) {
-            super(integer, CallerIdentity.forTest(Process.myUid(),
-                    Process.myPid() + (outOfProcess ? 1 : 0), "test", "test"), consumer);
+                Consumer<TestListenerRegistration> consumer) {
+            super(DIRECT_EXECUTOR, integer, consumer);
         }
     }
 
@@ -345,7 +343,7 @@
         }
 
         public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) {
-            addRegistration(consumer, new TestListenerRegistration(request, consumer, true));
+            addRegistration(consumer, new TestListenerRegistration(request, consumer));
         }
 
         public void removeListener(Consumer<TestListenerRegistration> consumer) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 57bfbf3..6acd9b6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -44,6 +44,7 @@
 import android.graphics.Region;
 import android.os.Looper;
 import android.view.MagnificationSpec;
+import android.view.accessibility.MagnificationAnimationCallback;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -99,12 +100,12 @@
     ValueAnimator.AnimatorListener mStateListener;
 
     FullScreenMagnificationController mFullScreenMagnificationController;
-    Runnable mEndCallback;
+    MagnificationAnimationCallback mAnimationCallback;
 
     @Before
     public void setUp() {
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
-        mEndCallback = Mockito.mock(Runnable.class);
+        mAnimationCallback = Mockito.mock(MagnificationAnimationCallback.class);
         // Pretending ID of the Thread associated with looper as main thread ID in controller
         when(mMockContext.getMainLooper()).thenReturn(looper);
         when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
@@ -323,7 +324,6 @@
         for (int i = 0; i < DISPLAY_COUNT; i++) {
             setScaleAndCenter_animated_stateChangesAndAnimationHappens(i);
             resetMockWindowManager();
-            Mockito.reset(mEndCallback);
         }
     }
 
@@ -336,7 +336,7 @@
         MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
 
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, scale,
-                newCenter.x, newCenter.y, mEndCallback, SERVICE_ID_1));
+                newCenter.x, newCenter.y, mAnimationCallback, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         assertEquals(newCenter.x, mFullScreenMagnificationController.getCenterX(displayId), 0.5);
@@ -365,18 +365,17 @@
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
         mStateListener.onAnimationEnd(mMockValueAnimator);
         verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
-        verify(mEndCallback).run();
+        verify(mAnimationCallback).onResult(true);
     }
 
     @Test
     public void testSetScaleAndCenterWithAnimation_sameSpec_noAnimationButInvokeEndCallback() {
         for (int i = 0; i < DISPLAY_COUNT; i++) {
-            setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(i);
-            Mockito.reset(mEndCallback);
+            setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(i);
         }
     }
 
-    private void setScaleAndCenter_sameSpec_noAnimationButInvokeEndCallback(int displayId) {
+    private void setScaleAndCenter_sameSpec_noAnimationButInvokeCallbacks(int displayId) {
         register(displayId);
         final PointF center = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         final float targetScale = 2.0f;
@@ -385,11 +384,11 @@
         mMessageCapturingHandler.sendAllMessages();
 
         assertFalse(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                targetScale, center.x, center.y, mEndCallback, SERVICE_ID_1));
+                targetScale, center.x, center.y, mAnimationCallback, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
 
         verify(mMockValueAnimator, never()).start();
-        verify(mEndCallback).run();
+        verify(mAnimationCallback).onResult(true);
     }
 
     @Test
@@ -673,38 +672,38 @@
     public void testReset_notMagnifying_noStateChangeButInvokeCallback() {
         for (int i = 0; i < DISPLAY_COUNT; i++) {
             reset_notMagnifying_noStateChangeButInvokeCallback(i);
-            Mockito.reset(mEndCallback);
         }
     }
 
     private void reset_notMagnifying_noStateChangeButInvokeCallback(int displayId) {
         register(displayId);
 
-        assertFalse(mFullScreenMagnificationController.reset(displayId, mEndCallback));
+        assertFalse(mFullScreenMagnificationController.reset(displayId, mAnimationCallback));
         mMessageCapturingHandler.sendAllMessages();
 
         verify(mMockAms, never()).notifyMagnificationChanged(eq(displayId),
                 any(Region.class), anyFloat(), anyFloat(), anyFloat());
-        verify(mEndCallback).run();
+        verify(mAnimationCallback).onResult(true);
     }
 
     @Test
-    public void testReset_Magnifying_resetsMagnificationAndInvokeLastEndCallback() {
+    public void testReset_Magnifying_resetsMagnificationAndInvokeCallbacks() {
         for (int i = 0; i < DISPLAY_COUNT; i++) {
-            reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(i);
+            reset_Magnifying_resetsMagnificationAndInvokeCallbacks(i);
         }
     }
 
-    private void reset_Magnifying_resetsMagnificationAndInvokeLastEndCallback(int displayId) {
+    private void reset_Magnifying_resetsMagnificationAndInvokeCallbacks(int displayId) {
         register(displayId);
         float scale = 2.5f;
         PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
         assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId,
-                scale, firstCenter.x, firstCenter.y, mEndCallback, SERVICE_ID_1));
+                scale, firstCenter.x, firstCenter.y, mAnimationCallback, SERVICE_ID_1));
         mMessageCapturingHandler.sendAllMessages();
         Mockito.reset(mMockValueAnimator);
         // Stubs the logic after the animation is started.
         doAnswer(invocation -> {
+            mStateListener.onAnimationCancel(mMockValueAnimator);
             mStateListener.onAnimationEnd(mMockValueAnimator);
             return null;
         }).when(mMockValueAnimator).cancel();
@@ -713,13 +712,14 @@
         float fraction = 0.33f;
         when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
         mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
-        Runnable lastEndCallback = Mockito.mock(Runnable.class);
+        MagnificationAnimationCallback lastAnimationCallback = Mockito.mock(
+                MagnificationAnimationCallback.class);
 
-        assertTrue(mFullScreenMagnificationController.reset(displayId, lastEndCallback));
+        assertTrue(mFullScreenMagnificationController.reset(displayId, lastAnimationCallback));
         mMessageCapturingHandler.sendAllMessages();
 
         // Verify expected actions.
-        verify(mEndCallback, never()).run();
+        verify(mAnimationCallback).onResult(false);
         verify(mMockValueAnimator).start();
         verify(mMockValueAnimator).cancel();
 
@@ -729,7 +729,7 @@
         mStateListener.onAnimationEnd(mMockValueAnimator);
 
         assertFalse(mFullScreenMagnificationController.isMagnifying(DISPLAY_0));
-        verify(lastEndCallback).run();
+        verify(lastAnimationCallback).onResult(true);
     }
 
     @Test
@@ -1142,6 +1142,7 @@
         verify(mMockValueAnimator).addListener(animatorListenerArgumentCaptor.capture());
         mStateListener = animatorListenerArgumentCaptor.getValue();
         Mockito.reset(mMockValueAnimator); // Ignore other initialization
+        Mockito.reset(mAnimationCallback);
     }
 
     private void zoomIn2xToMiddle(int displayId) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
index 42ba842f..f896d75 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
@@ -20,6 +20,7 @@
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.view.Display;
 import android.view.accessibility.IWindowMagnificationConnection;
@@ -69,13 +71,28 @@
             if (displayId != TEST_DISPLAY) {
                 throw new IllegalArgumentException("only support default display :" + displayId);
             }
-            computeMirrorWindowFrame(invocation.getArgument(1), invocation.getArgument(2));
-
+            computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3));
+            final RemoteCallback callback = invocation.getArgument(4);
+            if (callback != null) {
+                callback.sendResult(null);
+            }
             mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY,
                     mMirrorWindowFrame);
             return null;
         }).when(mConnection).enableWindowMagnification(anyInt(),
-                anyFloat(), anyFloat(), anyFloat());
+                anyFloat(), anyFloat(), anyFloat(), nullable(RemoteCallback.class));
+
+        doAnswer((invocation) -> {
+            final int displayId = invocation.getArgument(0);
+            if (displayId != TEST_DISPLAY) {
+                throw new IllegalArgumentException("only support default display :" + displayId);
+            }
+            final RemoteCallback callback = invocation.getArgument(1);
+            if (callback != null) {
+                callback.sendResult(null);
+            }
+            return null;
+        }).when(mConnection).disableWindowMagnification(anyInt(), nullable(RemoteCallback.class));
     }
 
     private void computeMirrorWindowFrame(float centerX, float centerY) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index 36b304b..9ef65d9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -19,6 +19,7 @@
 
 import static org.mockito.Mockito.verify;
 
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.view.Display;
@@ -43,18 +44,22 @@
     private IWindowMagnificationConnection mConnection;
     @Mock
     private IWindowMagnificationConnectionCallback mCallback;
+    @Mock
+    private RemoteCallback.OnResultListener mOnResultListener;
+    private RemoteCallback mRemoteCallback;
     private WindowMagnificationConnectionWrapper mConnectionWrapper;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+        mRemoteCallback = new RemoteCallback(mOnResultListener);
     }
 
     @Test
     public void enableWindowMagnification() throws RemoteException {
-        mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
-        verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
+        mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
+        verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback);
     }
 
     @Test
@@ -65,8 +70,8 @@
 
     @Test
     public void disableWindowMagnification() throws RemoteException {
-        mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY);
-        verify(mConnection).disableWindowMagnification(TEST_DISPLAY);
+        mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
+        verify(mConnection).disableWindowMagnification(TEST_DISPLAY, mRemoteCallback);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index bec9f26..a10e0ba 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -81,7 +81,7 @@
 
     @After
     public void tearDown() {
-        mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, true);
+        mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true);
     }
 
     @Test
@@ -225,7 +225,7 @@
             }
             break;
             case STATE_SHOW_MAGNIFIER: {
-                mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, false);
+                mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false);
             }
             break;
             case STATE_TWO_FINGERS_DOWN: {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 70e6a34..dcb1262 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -68,8 +68,12 @@
     private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT;
 
     private MockWindowMagnificationConnection mMockConnection;
-    @Mock private Context mContext;
-    @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal;
+    @Mock
+    private Context mContext;
+    @Mock
+    private StatusBarManagerInternal mMockStatusBarManagerInternal;
+    @Mock
+    private Runnable mEndCallback;
     private MockContentResolver mResolver;
     private WindowMagnificationManager mWindowMagnificationManager;
 
@@ -84,7 +88,7 @@
 
         when(mContext.getContentResolver()).thenReturn(mResolver);
         doAnswer((InvocationOnMock invocation) -> {
-            final boolean connect =  (Boolean) invocation.getArguments()[0];
+            final boolean connect = (Boolean) invocation.getArguments()[0];
             mWindowMagnificationManager.setConnection(
                     connect ? mMockConnection.getConnection() : null);
             return null;
@@ -158,32 +162,53 @@
     }
 
     @Test
-    public void enable_TestDisplay_enableWindowMagnification() throws RemoteException {
+    public void enable_hasConnection_enableWindowMagnification() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
 
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, 200f, 300f);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
 
         verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f,
-                200f, 300f);
+                200f, 300f, null);
     }
 
     @Test
-    public void disable_testDisplay_disableWindowMagnification() throws RemoteException {
+    public void enableWithCallback_hasConnection_enableWindowMagnification()
+            throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN);
 
-        mWindowMagnificationManager.disableWindowMagnifier(TEST_DISPLAY, false);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
+                mEndCallback);
 
-        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
+        verify(mEndCallback).run();
     }
 
     @Test
-    public void isWindowMagnifierEnabled_returnExpectedValue() {
+    public void disable_hasConnectionAndEnabled_disableWindowMagnification()
+            throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
 
+        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+    }
+
+    @Test
+    public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification() {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
+
+        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY,  false, mEndCallback);
+
+        verify(mEndCallback).run();
+    }
+
+    @Test
+    public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
 
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
 
         assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
     }
@@ -198,7 +223,7 @@
     @Test
     public void persistScale_setValue_expectedValueInProvider() {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
         mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
 
         mWindowMagnificationManager.persistScale(TEST_DISPLAY);
@@ -211,7 +236,7 @@
     @Test
     public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
 
         mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
 
@@ -221,7 +246,7 @@
     @Test
     public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.5f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
 
         mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f);
 
@@ -230,16 +255,17 @@
     }
 
     @Test
-    public void moveWindowMagnifier() throws RemoteException {
+    public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
 
-        mWindowMagnificationManager.moveWindowMagnifier(TEST_DISPLAY, 200, 300);
+        mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
         verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
     }
 
     @Test
-    public void showMagnificationButton() throws RemoteException {
+    public void showMagnificationButton_hasConnection_invokeConnectionMethod()
+            throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
 
         mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY,
@@ -252,9 +278,9 @@
     }
 
     @Test
-    public void pointersInWindow_returnCorrectValue() throws RemoteException {
+    public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3.0f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
         mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
                 new Rect(0, 0, 500, 500));
         PointF[] pointersLocation = new PointF[2];
@@ -268,7 +294,7 @@
     @Test
     public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
 
         mMockConnection.getDeathRecipient().binderDied();
 
@@ -280,11 +306,11 @@
             requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
             throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
 
         assertTrue(mWindowMagnificationManager.requestConnection(false));
 
-        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
+        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
         verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false);
     }
 
@@ -306,21 +332,24 @@
     @Test
     public void requestConnection_registerAndUnregisterBroadcastReceiver() {
         assertTrue(mWindowMagnificationManager.requestConnection(true));
-        verify(mContext).registerReceiver(any(BroadcastReceiver.class),  any(IntentFilter.class));
+        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
 
         assertTrue(mWindowMagnificationManager.requestConnection(false));
         verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
     }
 
     @Test
-    public void onReceiveScreenOff_removeMagnificationButtonAndDisableWindowMagnification()
+    public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
             throws RemoteException {
         mWindowMagnificationManager.requestConnection(true);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
         mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
                 new Intent(Intent.ACTION_SCREEN_OFF));
 
         verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
-        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
+        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
     }
 
     private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 609af8d..8d706cb 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -79,6 +79,23 @@
     }
 
     @Override
+    public int setDevicesRoleForCapturePreset(int capturePreset, int role,
+                                              @NonNull List<AudioDeviceAttributes> devices) {
+        return AudioSystem.AUDIO_STATUS_OK;
+    }
+
+    @Override
+    public int removeDevicesRoleForCapturePreset(
+            int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) {
+        return AudioSystem.AUDIO_STATUS_OK;
+    }
+
+    @Override
+    public int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
+        return AudioSystem.AUDIO_STATUS_OK;
+    }
+
+    @Override
     public int setParameters(String keyValuePairs) {
         return AudioSystem.AUDIO_STATUS_OK;
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index b306ff0..431cc27 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -16,7 +16,6 @@
 package com.android.server.devicepolicy;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -236,7 +235,7 @@
         }
         mUserInfos.add(uh);
         when(userManager.getUsers()).thenReturn(mUserInfos);
-        when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos);
+        when(userManager.getAliveUsers()).thenReturn(mUserInfos);
         when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true);
         when(userManager.getProfileParent(anyInt())).thenAnswer(
                 invocation -> {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index b1f3871..73dda07 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -19,6 +19,7 @@
 import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -28,18 +29,23 @@
 
 import android.app.PropertyInvalidatedCache;
 import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.Curve;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
 import android.hardware.input.InputManagerInternal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -282,6 +288,68 @@
     }
 
     /**
+     * Tests that there should be a display change notification to WindowManager to update its own
+     * internal state for things like display cutout when nonOverrideDisplayInfo is changed.
+     */
+    @Test
+    public void testShouldNotifyChangeWhenNonOverrideDisplayInfoChanged() throws Exception {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        registerDefaultDisplays(displayManager);
+        displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+        // Add the FakeDisplayDevice
+        FakeDisplayDevice displayDevice = new FakeDisplayDevice();
+        DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
+        displayDeviceInfo.width = 100;
+        displayDeviceInfo.height = 200;
+        final Rect zeroRect = new Rect();
+        displayDeviceInfo.displayCutout = new DisplayCutout(
+                Insets.of(0, 10, 0, 0),
+                zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect);
+        displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY;
+        displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
+        displayManager.handleDisplayDeviceAdded(displayDevice);
+
+        // Find the display id of the added FakeDisplayDevice
+        DisplayManagerService.BinderService bs = displayManager.new BinderService();
+        final int[] displayIds = bs.getDisplayIds();
+        assertTrue(displayIds.length > 0);
+        int displayId = Display.INVALID_DISPLAY;
+        for (int i = 0; i < displayIds.length; i++) {
+            DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayIds[i]);
+            if (displayDeviceInfo.equals(ddi)) {
+                displayId = displayIds[i];
+                break;
+            }
+        }
+        assertFalse(displayId == Display.INVALID_DISPLAY);
+
+        // Setup override DisplayInfo
+        DisplayInfo overrideInfo = bs.getDisplayInfo(displayId);
+        displayManager.setDisplayInfoOverrideFromWindowManagerInternal(displayId, overrideInfo);
+
+        Handler handler = displayManager.getDisplayHandler();
+        handler.runWithScissors(() -> {
+        }, 0 /* now */);
+
+        // register display listener callback
+        FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId);
+        bs.registerCallback(callback);
+
+        // Simulate DisplayDevice change
+        DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo();
+        displayDeviceInfo2.copyFrom(displayDeviceInfo);
+        displayDeviceInfo2.displayCutout = null;
+        displayDevice.setDisplayDeviceInfo(displayDeviceInfo2);
+        displayManager.handleDisplayDeviceChanged(displayDevice);
+
+        handler.runWithScissors(() -> {
+        }, 0 /* now */);
+        assertTrue(callback.mCalled);
+    }
+
+    /**
      * Tests that we get a Runtime exception when we cannot initialize the default display.
      */
     @Test
@@ -512,4 +580,42 @@
         // flush the handler
         handler.runWithScissors(() -> {}, 0 /* now */);
     }
+
+    private class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub {
+        int mDisplayId;
+        boolean mCalled = false;
+
+        FakeDisplayManagerCallback(int displayId) {
+            mDisplayId = displayId;
+        }
+
+        @Override
+        public void onDisplayEvent(int displayId, int event) {
+            if (displayId == mDisplayId && event == DisplayManagerGlobal.EVENT_DISPLAY_CHANGED) {
+                mCalled = true;
+            }
+        }
+    }
+
+    private class FakeDisplayDevice extends DisplayDevice {
+        private DisplayDeviceInfo mDisplayDeviceInfo;
+
+        FakeDisplayDevice() {
+            super(null, null, "");
+        }
+
+        public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
+            mDisplayDeviceInfo = displayDeviceInfo;
+        }
+
+        @Override
+        public boolean hasStableUniqueId() {
+            return false;
+        }
+
+        @Override
+        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+            return mDisplayDeviceInfo;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index ef2365e..0c35797 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.hdmi;
 
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -693,6 +694,84 @@
     }
 
     @Test
+    public void sendVolumeKeyEvent_toTv_activeSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(false);
+        mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress,
+                "HdmiCecLocalDevicePlaybackTest");
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
+    public void sendVolumeKeyEvent_toAudio_activeSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(true);
+        mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress,
+                "HdmiCecLocalDevicePlaybackTest");
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
+    public void sendVolumeKeyEvent_toTv_inactiveSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(false);
+        mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest");
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
+    public void sendVolumeKeyEvent_toAudio_inactiveSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(true);
+        mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest");
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
     public void handleSetStreamPath_broadcastsActiveSource() {
         HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
                 mPlaybackPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 775e887..31cf59e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -63,7 +63,7 @@
 @RunWith(JUnit4.class)
 public class HdmiControlServiceTest {
 
-    private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
+    private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDeviceSource {
 
         private boolean mCanGoToStandby;
         private boolean mIsStandby;
@@ -405,6 +405,83 @@
         assertThat(callback2.mVolumeControlEnabled).isTrue();
     }
 
+    @Test
+    public void setActiveSource_localDevice_playback() {
+        int physicalAddress = 0x1000;
+        mNativeWrapper.setPhysicalAddress(physicalAddress);
+
+        mHdmiControlService.setActiveSource(mMyPlaybackDevice.mAddress, physicalAddress,
+                "HdmiControlServiceTest");
+
+        assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo(
+                mMyPlaybackDevice.mAddress);
+        assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(
+                physicalAddress);
+        assertThat(mMyPlaybackDevice.mIsActiveSource).isTrue();
+        assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse();
+    }
+
+    @Test
+    public void setActiveSource_localDevice_audio() {
+        int physicalAddress = 0x1000;
+        mNativeWrapper.setPhysicalAddress(physicalAddress);
+
+        mHdmiControlService.setActiveSource(mMyAudioSystemDevice.mAddress, physicalAddress,
+                "HdmiControlServiceTest");
+
+        assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo(
+                mMyAudioSystemDevice.mAddress);
+        assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(
+                physicalAddress);
+        assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse();
+        assertThat(mMyAudioSystemDevice.mIsActiveSource).isTrue();
+    }
+
+    @Test
+    public void setActiveSource_remoteDevice() {
+        int physicalAddress = 0x1000;
+        mNativeWrapper.setPhysicalAddress(physicalAddress);
+
+        mHdmiControlService.setActiveSource(Constants.ADDR_TV, 0x0000, "HdmiControlServiceTest");
+
+        assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo(
+                Constants.ADDR_TV);
+        assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(0x000);
+        assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse();
+        assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse();
+    }
+
+    @Test
+    public void setActiveSource_nonCecDevice() {
+        int physicalAddress = 0x1000;
+        mNativeWrapper.setPhysicalAddress(physicalAddress);
+
+        mHdmiControlService.setActiveSource(Constants.ADDR_INVALID, 0x1234,
+                "HdmiControlServiceTest");
+
+        assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo(
+                Constants.ADDR_INVALID);
+        assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(0x1234);
+        assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse();
+        assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse();
+    }
+
+    @Test
+    public void setActiveSource_unknown() {
+        int physicalAddress = 0x1000;
+        mNativeWrapper.setPhysicalAddress(physicalAddress);
+
+        mHdmiControlService.setActiveSource(Constants.ADDR_INVALID,
+                Constants.INVALID_PHYSICAL_ADDRESS, "HdmiControlServiceTest");
+
+        assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo(
+                Constants.ADDR_INVALID);
+        assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(
+                Constants.INVALID_PHYSICAL_ADDRESS);
+        assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse();
+        assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse();
+    }
+
     private static class VolumeControlFeatureCallback extends
             IHdmiCecVolumeControlFeatureListener.Stub {
         boolean mCallbackReceived = false;
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index b2512d3..eec7d12 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -379,7 +379,26 @@
     }
 
     @Test
-    public void testForceQueryable_DoesntFilter() throws Exception {
+    public void testForceQueryable_SystemDoesntFilter() throws Exception {
+        final AppsFilter appsFilter =
+                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+                        mMockExecutor);
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        PackageSetting target = simulateAddPackage(appsFilter,
+                pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID,
+                setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+
+    @Test
+    public void testForceQueryable_NonSystemFilters() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
@@ -391,7 +410,7 @@
         PackageSetting calling = simulateAddPackage(appsFilter,
                 pkg("com.some.other.package"), DUMMY_CALLING_APPID);
 
-        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
                 SYSTEM_USER));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
new file mode 100644
index 0000000..7049efa1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 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.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+
+/**
+ * A test support class used for tracking a piece of state in test objects like fakes and mocks.
+ * State can optionally be initialized using {@link #init}, which sets the value to an initial
+ * value, but is not treated as a change. Calls to {@link #set} are tracked and can be checked for
+ * during tests. The change-tracking can be cleared by calling {@link #commitLatest}, which puts the
+ * object into an unchanged state and sets the initial value to the latest value passed to
+ * {@link #set}.
+ */
+public class TestState<T> {
+    private T mInitialValue;
+    private final ArrayList<T> mValues = new ArrayList<>(5);
+
+    /** Sets the initial value for the state. */
+    public void init(T value) {
+        mValues.clear();
+        mInitialValue = value;
+    }
+
+    /** Sets the latest value for the state. */
+    public void set(T value) {
+        mValues.add(value);
+    }
+
+    /** Returns {@code true} if {@link #set} has been called. */
+    public boolean hasBeenSet() {
+        return mValues.size() > 0;
+    }
+
+    /** Fails if {@link #set} has been called. */
+    public void assertHasNotBeenSet() {
+        assertFalse(hasBeenSet());
+    }
+
+    /** Fails if {@link #set} has not been called. */
+    public void assertHasBeenSet() {
+        assertTrue(hasBeenSet());
+    }
+
+    /**
+     * Clears tracked changes and re-initializes using the latest set value as the initial value.
+     */
+    public void commitLatest() {
+        if (hasBeenSet()) {
+            mInitialValue = mValues.get(mValues.size() - 1);
+            mValues.clear();
+        }
+    }
+
+    /** Asserts the latest value passed to {@link #set} equals {@code expected}. */
+    public void assertLatestEquals(T expected) {
+        assertEquals(expected, getLatest());
+    }
+
+    /** Asserts the number of times {@link #set} has been called. */
+    public void assertChangeCount(int expectedCount) {
+        assertEquals(expectedCount, mValues.size());
+    }
+
+    /**
+     * Returns the latest value passed to {@link #set}. If {@link #set} hasn't been called then the
+     * initial value is returned.
+     */
+    public T getLatest() {
+        if (hasBeenSet()) {
+            return mValues.get(mValues.size() - 1);
+        }
+        return mInitialValue;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 2bee5e5..1cdf193 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -54,7 +54,6 @@
 import java.io.StringWriter;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -994,55 +993,6 @@
         }
     }
 
-    /** Some piece of state that tests want to track. */
-    private static class TestState<T> {
-        private T mInitialValue;
-        private LinkedList<T> mValues = new LinkedList<>();
-
-        void init(T value) {
-            mValues.clear();
-            mInitialValue = value;
-        }
-
-        void set(T value) {
-            mValues.addFirst(value);
-        }
-
-        boolean hasBeenSet() {
-            return mValues.size() > 0;
-        }
-
-        void assertHasNotBeenSet() {
-            assertFalse(hasBeenSet());
-        }
-
-        void assertHasBeenSet() {
-            assertTrue(hasBeenSet());
-        }
-
-        void commitLatest() {
-            if (hasBeenSet()) {
-                mInitialValue = mValues.getLast();
-                mValues.clear();
-            }
-        }
-
-        void assertLatestEquals(T expected) {
-            assertEquals(expected, getLatest());
-        }
-
-        void assertChangeCount(int expectedCount) {
-            assertEquals(expectedCount, mValues.size());
-        }
-
-        public T getLatest() {
-            if (hasBeenSet()) {
-                return mValues.getFirst();
-            }
-            return mInitialValue;
-        }
-    }
-
     /**
      * A "fluent" class allows reuse of code in tests: initialization, simulation and verification
      * logic.
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 7c7b1a2..2aeab20 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -139,10 +139,13 @@
     private AppStandbyController mController;
 
     private CountDownLatch mStateChangedLatch = new CountDownLatch(1);
+    private String mLatchPkgName = null;
     private AppIdleStateChangeListener mListener = new AppIdleStateChangeListener() {
         @Override
         public void onAppIdleStateChanged(String packageName, int userId,
                 boolean idle, int bucket, int reason) {
+            // Ignore events not related to mLatchPkgName, if set.
+            if (mLatchPkgName != null && !mLatchPkgName.equals(packageName)) return;
             mStateChangedLatch.countDown();
         }
     };
@@ -374,6 +377,7 @@
                         mInjector.mElapsedRealtime, false));
 
         controller.addListener(mListener);
+        mLatchPkgName = null;
         return controller;
     }
 
@@ -1377,7 +1381,7 @@
 
     @Test
     public void testUnexemptedSyncScheduled() throws Exception {
-        mStateChangedLatch = new CountDownLatch(1);
+        rearmLatch(PACKAGE_1);
         mController.addListener(mListener);
         assertEquals("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER,
                 getStandbyBucket(mController, PACKAGE_1));
@@ -1389,7 +1393,7 @@
 
         setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
 
-        mStateChangedLatch = new CountDownLatch(1);
+        rearmLatch(PACKAGE_1);
         mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false);
         mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS);
         assertEquals("Unexempted sync scheduled should not elevate a non Never bucket",
@@ -1400,7 +1404,7 @@
     public void testExemptedSyncScheduled() throws Exception {
         setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
         mInjector.mDeviceIdleMode = true;
-        mStateChangedLatch = new CountDownLatch(1);
+        rearmLatch(PACKAGE_1);
         mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
         mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS);
         assertEquals("Exempted sync scheduled in doze should set bucket to working set",
@@ -1408,7 +1412,7 @@
 
         setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
         mInjector.mDeviceIdleMode = false;
-        mStateChangedLatch = new CountDownLatch(1);
+        rearmLatch(PACKAGE_1);
         mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
         mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS);
         assertEquals("Exempted sync scheduled while not in doze should set bucket to active",
@@ -1558,10 +1562,19 @@
     }
 
     private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception {
-        mStateChangedLatch = new CountDownLatch(1);
+        rearmLatch(pkg);
         mController.setAppStandbyBucket(pkg, user, bucket, reason);
         mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
         assertEquals("Failed to set package bucket", bucket,
                 getStandbyBucket(mController, PACKAGE_1));
     }
+
+    private void rearmLatch(String pkgName) {
+        mLatchPkgName = pkgName;
+        mStateChangedLatch = new CountDownLatch(1);
+    }
+
+    private void rearmLatch() {
+        rearmLatch(null);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 99433a6..d7e431f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -978,6 +978,7 @@
 
         assertFalse(services.isSameUser(service, 0));
         assertTrue(services.isSameUser(service, 10));
+        assertTrue(services.isSameUser(service, UserHandle.USER_ALL));
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index ab4dc47..5796e84 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -103,7 +103,7 @@
             when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
         }
         when(mUm.getUsers()).thenReturn(users);
-        when(mUm.getUsers(anyBoolean())).thenReturn(users);
+        when(mUm.getAliveUsers()).thenReturn(users);
         IntArray profileIds = new IntArray();
         profileIds.add(0);
         profileIds.add(11);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9319bea..8644719 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5058,7 +5058,7 @@
                 10, 10, r.getKey(), actionIndex, action, notificationVisibility,
                 generatedByAssistant);
         verify(mAssistants).notifyAssistantActionClicked(
-                eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant));
+                eq(r.getSbn()), eq(action), eq(generatedByAssistant));
 
         assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(
@@ -5082,7 +5082,7 @@
                 10, 10, r.getKey(), actionIndex, action, notificationVisibility,
                 generatedByAssistant);
         verify(mAssistants).notifyAssistantActionClicked(
-                eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant));
+                eq(r.getSbn()), eq(action), eq(generatedByAssistant));
 
         assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(
@@ -6948,4 +6948,63 @@
         assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS + 1,
                 mService.getNotificationRecordCount());
     }
+
+    @Test
+    public void testIsVisibleToListener_notEnabled() {
+        StatusBarNotification sbn = mock(StatusBarNotification.class);
+        when(sbn.getUserId()).thenReturn(10);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class);
+        info.userid = 10;
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        when(assistant.isSameUser(anyInt())).thenReturn(true);
+        when(info.enabledAndUserMatches(info.userid)).thenReturn(false);
+        when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
+
+        assertFalse(mService.isVisibleToListener(sbn, info));
+    }
+
+    @Test
+    public void testIsVisibleToListener_noAssistant() {
+        StatusBarNotification sbn = mock(StatusBarNotification.class);
+        when(sbn.getUserId()).thenReturn(10);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        info.userid = 10;
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
+        when(mAssistants.checkServiceTokenLocked(any())).thenReturn(null);
+
+        assertTrue(mService.isVisibleToListener(sbn, info));
+    }
+
+    @Test
+    public void testIsVisibleToListener_assistant_differentUser() {
+        StatusBarNotification sbn = mock(StatusBarNotification.class);
+        when(sbn.getUserId()).thenReturn(10);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class);
+        info.userid = 0;
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        when(assistant.isSameUser(anyInt())).thenReturn(true);
+        when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
+        when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
+
+        assertFalse(mService.isVisibleToListener(sbn, info));
+    }
+
+    @Test
+    public void testIsVisibleToListener_assistant_sameUser() {
+        StatusBarNotification sbn = mock(StatusBarNotification.class);
+        when(sbn.getUserId()).thenReturn(10);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class);
+        info.userid = 10;
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        when(assistant.isSameUser(anyInt())).thenReturn(true);
+        when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
+        when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
+
+        assertTrue(mService.isVisibleToListener(sbn, info));
+    }
+
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
new file mode 100644
index 0000000..f6c854e
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PackageMatchingCacheTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 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.slice;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.slice.SliceManagerService.PackageMatchingCache;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Supplier;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class PackageMatchingCacheTest extends UiServiceTestCase {
+
+    private final Supplier<String> supplier = mock(Supplier.class);
+    private final PackageMatchingCache cache = new PackageMatchingCache(supplier);
+
+    @Test
+    public void testNulls() {
+        // Doesn't get for a null input
+        cache.matches(null);
+        verify(supplier, never()).get();
+
+        // Gets once valid input in sent.
+        cache.matches("");
+        verify(supplier).get();
+    }
+
+    @Test
+    public void testCaching() {
+        when(supplier.get()).thenReturn("ret.pkg");
+
+        assertTrue(cache.matches("ret.pkg"));
+        assertTrue(cache.matches("ret.pkg"));
+        assertTrue(cache.matches("ret.pkg"));
+
+        verify(supplier, times(1)).get();
+    }
+
+    @Test
+    public void testGetOnFailure() {
+        when(supplier.get()).thenReturn("ret.pkg");
+        assertTrue(cache.matches("ret.pkg"));
+
+        when(supplier.get()).thenReturn("other.pkg");
+        assertTrue(cache.matches("other.pkg"));
+        verify(supplier, times(2)).get();
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index cf1c36c..a443695 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -90,6 +90,8 @@
 
     @Test
     public void testAddPinCreatesPinned() throws RemoteException {
+        doReturn("pkg").when(mService).getDefaultHome(anyInt());
+
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
         verify(mService, times(1)).createPinnedSlice(eq(maybeAddUserId(TEST_URI, 0)), anyString());
@@ -97,6 +99,8 @@
 
     @Test
     public void testRemovePinDestroysPinned() throws RemoteException {
+        doReturn("pkg").when(mService).getDefaultHome(anyInt());
+
         mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
 
         when(mCreatedSliceState.unpin(eq("pkg"), eq(mToken))).thenReturn(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 1eb45d5..cf07183 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -67,7 +67,7 @@
         final TaskDisplayArea taskDisplayAreas =
                 mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea();
         final Task stack =
-                new StackBuilder(mRootWindowContainer).setOnTop(!ON_TOP).build();
+                new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
         final Task prevFocusedStack = taskDisplayAreas.getFocusedStack();
 
         stack.moveToFront("moveStackToFront");
@@ -90,7 +90,7 @@
         final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea()
                 .createStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task pinnedTask = new TaskBuilder(mAtm.mStackSupervisor)
-                .setStack(pinnedStack).build();
+                .setParentTask(pinnedStack).build();
         new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
                 .setTask(pinnedTask).build();
         pinnedStack.moveToFront("movePinnedStackToFront");
@@ -144,7 +144,7 @@
         doReturn(false).when(display).shouldDestroyContentOnRemove();
 
         // Put home stack on the display.
-        final Task homeStack = new StackBuilder(mRootWindowContainer)
+        final Task homeStack = new TaskBuilder(mSupervisor)
                 .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
 
         // Put a finishing standard activity which will be reparented.
@@ -163,7 +163,7 @@
         final Task fullscreenStack = display.getDefaultTaskDisplayArea().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task fullscreenTask = new TaskBuilder(mAtm.mStackSupervisor)
-                .setStack(fullscreenStack).build();
+                .setParentTask(fullscreenStack).build();
         new ActivityBuilder(mAtm).setTask(fullscreenTask).build();
         return fullscreenStack;
     }
@@ -175,12 +175,11 @@
     public void testTopRunningActivity() {
         final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
         final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord activity = stack.getTopNonFinishingActivity();
 
         // Create empty stack on top.
-        final Task emptyStack =
-                new StackBuilder(mRootWindowContainer).setCreateActivity(false).build();
+        final Task emptyStack = new TaskBuilder(mSupervisor).build();
 
         // Make sure the top running activity is not affected when keyguard is not locked.
         assertTopRunningActivity(activity, display);
@@ -322,10 +321,10 @@
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task task1 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack1).build();
-        final Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack2).build();
-        final Task task3 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack3).build();
-        final Task task4 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack4).build();
+        final Task task1 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack1).build();
+        final Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack2).build();
+        final Task task3 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack3).build();
+        final Task task4 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack4).build();
 
         // Reordering stacks while removing stacks.
         doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index eb78172..e860f25 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -341,9 +341,8 @@
     public void testConsecutiveLaunchOnDifferentDisplay() {
         onActivityLaunched(mTopActivity);
 
-        final Task stack = new StackBuilder(mRootWindowContainer)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setDisplay(addNewDisplayContentAt(DisplayContent.POSITION_BOTTOM))
-                .setCreateActivity(false)
                 .build();
         final ActivityRecord activityOnNewDisplay = new ActivityBuilder(mAtm)
                 .setStack(stack)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 59f0a79..09375db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -131,7 +131,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mStack = new StackBuilder(mRootWindowContainer).build();
+        mStack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         mTask = mStack.getBottomMostTask();
         mActivity = mTask.getTopNonFinishingActivity();
 
@@ -172,7 +172,7 @@
 
     @Test
     public void testNoCleanupMovingActivityInSameStack() {
-        final Task newTask = new TaskBuilder(mAtm.mStackSupervisor).setStack(mStack).build();
+        final Task newTask = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(mStack).build();
         mActivity.reparent(newTask, 0, null /*reason*/);
         verify(mStack, times(0)).cleanUpActivityReferences(any());
     }
@@ -262,7 +262,7 @@
 
         // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options.
         // Pending options should be cleared for only ActivityRecord that was applied
-        Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setStack(mStack).build();
+        Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(mStack).build();
         activity2 = new ActivityBuilder(mAtm).setTask(task2).build();
         activity2.updateOptionsLocked(activityOptions);
         mActivity.updateOptionsLocked(activityOptions);
@@ -415,12 +415,12 @@
     public void ignoreRequestedOrientationInFreeformWindows() {
         mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         final Rect stableRect = new Rect();
-        mStack.getDisplay().mDisplayContent.getStableRect(stableRect);
+        mStack.mDisplayContent.getStableRect(stableRect);
 
         // Carve out non-decor insets from stableRect
         final Rect insets = new Rect();
-        final DisplayInfo displayInfo = mStack.getDisplay().getDisplayInfo();
-        final DisplayPolicy policy = mStack.getDisplay().getDisplayPolicy();
+        final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo();
+        final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy();
         policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
                 displayInfo.logicalHeight, displayInfo.displayCutout, insets);
         policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
@@ -454,12 +454,12 @@
     public void ignoreRequestedOrientationInSplitWindows() {
         mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         final Rect stableRect = new Rect();
-        mStack.getDisplay().getStableRect(stableRect);
+        mStack.mDisplayContent.getStableRect(stableRect);
 
         // Carve out non-decor insets from stableRect
         final Rect insets = new Rect();
-        final DisplayInfo displayInfo = mStack.getDisplay().getDisplayInfo();
-        final DisplayPolicy policy = mStack.getDisplay().getDisplayPolicy();
+        final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo();
+        final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy();
         policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
                 displayInfo.logicalHeight, displayInfo.displayCutout, insets);
         policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation);
@@ -548,7 +548,7 @@
                 .build();
         mActivity.setState(Task.ActivityState.STOPPED, "Testing");
 
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         try {
             doReturn(false).when(stack).isTranslucent(any());
             assertFalse(mStack.shouldBeVisible(null /* starting */));
@@ -756,14 +756,14 @@
     @Test
     public void testFinishActivityIfPossible_adjustStackOrder() {
         // Prepare the stacks with order (top to bottom): mStack, stack1, stack2.
-        final Task stack1 = new StackBuilder(mRootWindowContainer).build();
+        final Task stack1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         mStack.moveToFront("test");
         // The stack2 is needed here for moving back to simulate the
         // {@link DisplayContent#mPreferredTopFocusableStack} is cleared, so
         // {@link DisplayContent#getFocusedStack} will rely on the order of focusable-and-visible
         // stacks. Then when mActivity is finishing, its stack will be invisible (no running
         // activities in the stack) that is the key condition to verify.
-        final Task stack2 = new StackBuilder(mRootWindowContainer).build();
+        final Task stack2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         stack2.moveToBack("test", stack2.getBottomMostTask());
 
         assertTrue(mStack.isTopStackInDisplayArea());
@@ -844,7 +844,7 @@
                 FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */));
         assertEquals(PAUSING, mActivity.getState());
         verify(mActivity).setVisibility(eq(false));
-        verify(mActivity.getDisplay().mDisplayContent)
+        verify(mActivity.mDisplayContent)
                 .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
     }
 
@@ -888,9 +888,9 @@
         mActivity.finishIfPossible("test", false /* oomAdj */);
 
         verify(mActivity).setVisibility(eq(false));
-        verify(mActivity.getDisplay().mDisplayContent)
+        verify(mActivity.mDisplayContent)
                 .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
-        verify(mActivity.getDisplay().mDisplayContent, never()).executeAppTransition();
+        verify(mActivity.mDisplayContent, never()).executeAppTransition();
     }
 
     /**
@@ -904,9 +904,9 @@
         mActivity.finishIfPossible("test", false /* oomAdj */);
 
         verify(mActivity, atLeast(1)).setVisibility(eq(false));
-        verify(mActivity.getDisplay().mDisplayContent)
+        verify(mActivity.mDisplayContent)
                 .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
-        verify(mActivity.getDisplay().mDisplayContent).executeAppTransition();
+        verify(mActivity.mDisplayContent).executeAppTransition();
     }
 
     /**
@@ -922,7 +922,7 @@
 
         mActivity.finishIfPossible("test", false /* oomAdj */);
 
-        verify(mActivity.getDisplay().mDisplayContent, never())
+        verify(mActivity.mDisplayContent, never())
                 .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
     }
 
@@ -973,7 +973,7 @@
 
         // Simulates that {@code currentTop} starts an existing activity from background (so its
         // state is stopped) and the starting flow just goes to place it at top.
-        final Task nextStack = new StackBuilder(mRootWindowContainer).build();
+        final Task nextStack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord nextTop = nextStack.getTopNonFinishingActivity();
         nextTop.setState(STOPPED, "test");
 
@@ -1095,7 +1095,7 @@
 
         // Add another stack to become focused and make the activity there visible. This way it
         // simulates finishing in non-focused stack in split-screen.
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord focusedActivity = stack.getTopMostActivity();
         focusedActivity.nowVisible = true;
         focusedActivity.mVisibleRequested = true;
@@ -1166,7 +1166,7 @@
         // Finish the second activity
         secondActivity.finishing = true;
         secondActivity.completeFinishing("test");
-        verify(secondActivity.getDisplay()).ensureActivitiesVisible(null /* starting */,
+        verify(secondActivity.mDisplayContent).ensureActivitiesVisible(null /* starting */,
                 0 /* configChanges */ , false /* preserveWindows */,
                 true /* notifyClients */);
 
@@ -1174,7 +1174,7 @@
         firstActivity.finishing = true;
         firstActivity.mVisibleRequested = true;
         firstActivity.completeFinishing("test");
-        verify(firstActivity.getDisplay(), times(2)).ensureActivitiesVisible(null /* starting */,
+        verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */,
                 0 /* configChanges */ , false /* preserveWindows */,
                 true /* notifyClients */);
     }
@@ -1581,7 +1581,7 @@
 
         // Create a new task with custom config to reparent the activity to.
         final Task newTask =
-                new TaskBuilder(mSupervisor).setStack(initialTask.getRootTask()).build();
+                new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
         final Configuration newConfig = newTask.getConfiguration();
         newConfig.densityDpi += 100;
         newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1613,7 +1613,7 @@
 
         // Create a new task with custom config to reparent the second activity to.
         final Task newTask =
-                new TaskBuilder(mSupervisor).setStack(initialTask.getRootTask()).build();
+                new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
         final Configuration newConfig = newTask.getConfiguration();
         newConfig.densityDpi += 100;
         newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1750,7 +1750,7 @@
         }
         final Task stack = display.getDefaultTaskDisplayArea()
                 .createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         return new ActivityBuilder(mAtm).setTask(task).setUseProcess(process).build();
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 27e2d13..f9ad49b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -115,8 +115,8 @@
     public void testHandleNonResizableTaskOnSecondaryDisplay() {
         // Create an unresizable task on secondary display.
         final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
-        final Task stack = new StackBuilder(mRootWindowContainer)
-                .setDisplay(newDisplay).build();
+        final Task stack = new TaskBuilder(mSupervisor)
+                .setDisplay(newDisplay).setCreateActivity(true).build();
         final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
         final Task task = unresizableActivity.getTask();
         unresizableActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index e2948a7..4951658 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -100,7 +100,7 @@
         mStack = mDefaultTaskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         spyOn(mStack);
-        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
+        mTask = new TaskBuilder(mSupervisor).setParentTask(mStack).build();
     }
 
     @Test
@@ -330,7 +330,7 @@
                 targetActivity);
         final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME,
                 aliasActivity);
-        final Task task = new TaskBuilder(mAtm.mStackSupervisor).setStack(mStack).build();
+        final Task task = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(mStack).build();
         task.origActivity = alias;
         task.realActivity = target;
         new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity(
@@ -995,7 +995,7 @@
             mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task,
                     false /* includingParents */);
         } else {
-            task = new StackBuilder(mRootWindowContainer)
+            task = new TaskBuilder(mSupervisor)
                     .setTaskDisplayArea(taskDisplayArea)
                     .setWindowingMode(windowingMode)
                     .setActivityType(activityType)
@@ -1202,19 +1202,22 @@
     @Test
     public void testShouldSleepActivities() {
         // When focused activity and keyguard is going away, we should not sleep regardless
-        // of the display state
+        // of the display state, but keyguard-going-away should only take effects on default
+        // display since there is no keyguard on secondary displays (yet).
         verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, false /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
+        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+                true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */);
 
         // When not the focused stack, defer to display sleeping state.
         verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
 
         // If keyguard is going away, defer to the display sleeping state.
         verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
         verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                false /* displaySleeping */, false /* expected*/);
+                false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
     }
 
     @Test
@@ -1423,11 +1426,13 @@
     }
 
     private void verifyShouldSleepActivities(boolean focusedStack,
-            boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
+            boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
+            boolean expected) {
         final DisplayContent display = mock(DisplayContent.class);
         final KeyguardController keyguardController = mSupervisor.getKeyguardController();
+        display.isDefaultDisplay = isDefaultDisplay;
 
-        doReturn(display).when(mStack).getDisplay();
+        mStack.mDisplayContent = display;
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
         doReturn(displaySleeping).when(display).isSleeping();
         doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index e5c9ecc..076047b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -217,7 +217,10 @@
 
         // Create source token
         final ActivityBuilder builder = new ActivityBuilder(service).setTask(
-                new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build());
+                new TaskBuilder(service.mStackSupervisor)
+                        .setVoiceSession(voiceSession)
+                        .setCreateParentTask(true)
+                        .build());
 
         if (aInfo != null) {
             aInfo.applicationInfo = new ApplicationInfo();
@@ -706,7 +709,9 @@
     @Test
     public void testBringTaskToFrontWhenFocusedStackIsFinising() {
         // Put 2 tasks in the same stack (simulate the behavior of home stack).
+        final Task rootTask = new TaskBuilder(mSupervisor).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setStack(rootTask)
                 .setCreateTask(true).build();
         new ActivityBuilder(mAtm)
                 .setStack(activity.getRootTask())
@@ -792,7 +797,7 @@
         // Create another activity on top of the secondary display.
         final Task topStack = secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
+        final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build();
         new ActivityBuilder(mAtm).setTask(topTask).build();
 
         // Start activity with the same intent as {@code singleTaskActivity} on secondary display.
@@ -851,7 +856,7 @@
                 DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
         final Task task = new TaskBuilder(mSupervisor)
                 .setComponent(componentName)
-                .setStack(stack)
+                .setParentTask(stack)
                 .build();
         return new ActivityBuilder(mAtm)
                 .setComponent(componentName)
@@ -890,7 +895,7 @@
                 .execute();
 
         // Ensure the activity is moved to secondary display.
-        assertEquals(secondaryDisplay, topActivity.getDisplay());
+        assertEquals(secondaryDisplay, topActivity.mDisplayContent);
     }
 
     /**
@@ -987,7 +992,7 @@
         final ActivityStarter starter = prepareStarter(0 /* flags */);
         starter.mStartActivity = new ActivityBuilder(mAtm).build();
         final Task task = new TaskBuilder(mAtm.mStackSupervisor)
-                .setStack(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
+                .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .setUserId(10)
                 .build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 5676100..0311657 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -73,7 +73,7 @@
     /** Verify that activity is finished correctly upon request. */
     @Test
     public void testActivityFinish() {
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
         assertTrue("Activity must be finished", mAtm.finishActivity(activity.appToken,
                 0 /* resultCode */, null /* resultData */,
@@ -87,7 +87,7 @@
 
     @Test
     public void testOnPictureInPictureRequested() throws RemoteException {
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
         final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
         doReturn(mockLifecycleManager).when(mAtm).getLifecycleManager();
@@ -106,7 +106,7 @@
 
     @Test(expected = IllegalStateException.class)
     public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
         ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
         doReturn(false).when(activity).inPinnedWindowingMode();
@@ -120,7 +120,7 @@
 
     @Test(expected = IllegalStateException.class)
     public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException {
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
         ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager();
         doReturn(true).when(activity).inPinnedWindowingMode();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index f8baf84..7f90426 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -28,6 +28,7 @@
 import static android.os.Build.VERSION_CODES.P;
 import static android.os.Build.VERSION_CODES.Q;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_PRIVATE;
 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
@@ -95,6 +96,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.view.DisplayCutout;
+import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IDisplayWindowRotationCallback;
 import android.view.IDisplayWindowRotationController;
@@ -847,17 +849,17 @@
         dc.getDisplayRotation().setFixedToUserRotation(
                 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
 
-        final Task stack =
-                new StackBuilder(mWm.mAtmService.mRootWindowContainer)
-                        .setDisplay(dc)
-                        .build();
+        final Task stack = new TaskBuilder(mSupervisor)
+                .setDisplay(dc)
+                .setCreateActivity(true)
+                .build();
         doReturn(true).when(stack).isVisible();
 
-        final Task freeformStack =
-                new StackBuilder(mWm.mAtmService.mRootWindowContainer)
-                        .setDisplay(dc)
-                        .setWindowingMode(WINDOWING_MODE_FREEFORM)
-                        .build();
+        final Task freeformStack = new TaskBuilder(mSupervisor)
+                .setDisplay(dc)
+                .setCreateActivity(true)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM)
+                .build();
         doReturn(true).when(freeformStack).isVisible();
         freeformStack.getTopChild().setBounds(100, 100, 300, 400);
 
@@ -879,8 +881,8 @@
                 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
         final int newOrientation = getRotatedOrientation(dc);
 
-        final Task stack = new StackBuilder(mWm.mAtmService.mRootWindowContainer)
-                .setDisplay(dc).build();
+        final Task stack = new TaskBuilder(mSupervisor)
+                .setDisplay(dc).setCreateActivity(true).build();
         final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
 
         activity.setRequestedOrientation(newOrientation);
@@ -898,8 +900,8 @@
                 IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
         final int newOrientation = getRotatedOrientation(dc);
 
-        final Task stack = new StackBuilder(mWm.mAtmService.mRootWindowContainer)
-                .setDisplay(dc).build();
+        final Task stack = new TaskBuilder(mSupervisor)
+                .setDisplay(dc).setCreateActivity(true).build();
         final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity();
 
         activity.setRequestedOrientation(newOrientation);
@@ -1209,8 +1211,8 @@
         verify(t, never()).setPosition(any(), eq(0), eq(0));
 
         // Launch another activity before the transition is finished.
-        final ActivityRecord app2 = new StackBuilder(mWm.mRoot)
-                .setDisplay(mDisplayContent).build().getTopMostActivity();
+        final ActivityRecord app2 = new TaskBuilder(mSupervisor)
+                .setDisplay(mDisplayContent).setCreateActivity(true).build().getTopMostActivity();
         app2.setVisible(false);
         mDisplayContent.mOpeningApps.add(app2);
         app2.setRequestedOrientation(newOrientation);
@@ -1508,7 +1510,7 @@
     @Test
     public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
         final DisplayContent dc = createNewDisplay();
-        final Task stack = new StackBuilder(mWm.mAtmService.mRootWindowContainer)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setDisplay(dc)
                 .build();
         doAnswer(invocation -> {
@@ -1522,6 +1524,28 @@
         dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
     }
 
+    @Test
+    public void testForceDesktopMode() {
+        mWm.mForceDesktopModeOnExternalDisplays = true;
+        // Not applicable for default display
+        assertFalse(mDefaultDisplay.forceDesktopMode());
+
+        // Not applicable for private secondary display.
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.copyFrom(mDisplayInfo);
+        displayInfo.flags = FLAG_PRIVATE;
+        final DisplayContent privateDc = createNewDisplay(displayInfo);
+        assertFalse(privateDc.forceDesktopMode());
+
+        // Applicable for public secondary display.
+        final DisplayContent publicDc = createNewDisplay();
+        assertTrue(publicDc.forceDesktopMode());
+
+        // Make sure forceDesktopMode() is false when the force config is disabled.
+        mWm.mForceDesktopModeOnExternalDisplays = false;
+        assertFalse(publicDc.forceDesktopMode());
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 2d834ac..94e4041 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -26,8 +26,6 @@
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
-import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -36,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -371,6 +370,24 @@
     }
 
     @Test
+    public void layoutWindowLw_insetParentFrameByIme() {
+        final InsetsState state =
+                mDisplayContent.getInsetsStateController().getRawInsetsState();
+        state.getSource(InsetsState.ITYPE_IME).setVisible(true);
+        state.getSource(InsetsState.ITYPE_IME).setFrame(
+                0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+        mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
+        mWindow.mBehindIme = true;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, IME_HEIGHT);
+    }
+
+    @Test
     public void layoutWindowLw_fitDisplayCutout() {
         addDisplayCutout();
 
@@ -475,7 +492,6 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
         mWindow.mAttrs.setFitInsetsTypes(
                 mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
         addWindow(mWindow);
@@ -497,9 +513,9 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
         mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false);
         addWindow(mWindow);
 
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -519,9 +535,9 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
         mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         addWindow(mWindow);
 
@@ -585,7 +601,6 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
         mWindow.mAttrs.setFitInsetsTypes(
                 mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
         addWindow(mWindow);
@@ -626,7 +641,6 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
         mWindow.mAttrs.setFitInsetsTypes(
                 mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index b50530e..b77d21c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -27,11 +27,9 @@
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -39,7 +37,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
@@ -47,7 +44,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -235,18 +231,6 @@
         assertEquals(mAppWindow, policy.getTopFullscreenOpaqueWindow());
     }
 
-    @Test
-    public void testShouldShowToastWhenScreenLocked() {
-        final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
-        final WindowState activity = createApplicationWindow();
-        final WindowState toast = createToastWindow();
-
-        policy.adjustWindowParamsLw(toast, toast.mAttrs, 0 /* callingPid */, 0 /* callingUid */);
-
-        assertTrue(policy.canToastShowWhenLocked(0 /* callingUid */));
-        assertNotEquals(0, toast.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED);
-    }
-
     @Test(expected = RuntimeException.class)
     public void testMainAppWindowDisallowFitSystemWindowTypes() {
         final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
@@ -257,16 +241,6 @@
                 0 /* callingUid */);
     }
 
-    private WindowState createToastWindow() {
-        final WindowState win = createWindow(null, TYPE_TOAST, "Toast");
-        final WindowManager.LayoutParams attrs = win.mAttrs;
-        attrs.width = WRAP_CONTENT;
-        attrs.height = WRAP_CONTENT;
-        attrs.flags = FLAG_KEEP_SCREEN_ON | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE;
-        attrs.format = PixelFormat.TRANSLUCENT;
-        return win;
-    }
-
     private WindowState createApplicationWindow() {
         final WindowState win = createWindow(null, TYPE_APPLICATION, "Application");
         final WindowManager.LayoutParams attrs = win.mAttrs;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index b4e1c37..af8cb02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -62,7 +62,7 @@
     static final int STATUS_BAR_HEIGHT = 10;
     static final int NAV_BAR_HEIGHT = 15;
     static final int DISPLAY_CUTOUT_HEIGHT = 8;
-    static final int INPUT_METHOD_WINDOW_TOP = 585;
+    static final int IME_HEIGHT = 415;
 
     DisplayPolicy mDisplayPolicy;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 18a2d13..d701a9d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -116,7 +116,7 @@
 
         Task stack = mTestDisplay.getDefaultTaskDisplayArea()
                 .createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
-        mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setStack(stack)
+        mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setParentTask(stack)
                 .build();
         mTestTask.mUserId = TEST_USER_ID;
         mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS;
@@ -342,7 +342,7 @@
         final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor)
                 .setComponent(ALTERNATIVE_COMPONENT)
                 .setUserId(TEST_USER_ID)
-                .setStack(stack)
+                .setParentTask(stack)
                 .build();
         anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500);
@@ -354,7 +354,7 @@
         final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor)
                 .setComponent(TEST_COMPONENT)
                 .setUserId(ALTERNATIVE_USER_ID)
-                .setStack(stack)
+                .setParentTask(stack)
                 .build();
         anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 54c7f27..253fbae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -28,6 +28,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -331,10 +333,10 @@
         // other task
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .setStack(mTaskContainer.getRootHomeTask()).build();
+                .setParentTask(mTaskContainer.getRootHomeTask()).build();
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .setStack(mStack).build();
+                .setParentTask(mStack).build();
         mRecentTasks.add(task1);
         mRecentTasks.add(task2);
         assertThat(mCallbacksRecorder.mAdded).hasSize(2);
@@ -350,7 +352,7 @@
         // and we want to ensure that a new task will match a restored task
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .build();
         setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
         assertThat(task1.getActivityType()).isEqualTo(ACTIVITY_TYPE_UNDEFINED);
@@ -359,7 +361,7 @@
 
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .build();
         assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType());
         mRecentTasks.add(task2);
@@ -374,7 +376,7 @@
     public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() {
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .setUserId(TEST_USER_0_ID)
                 .build();
         setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
@@ -384,7 +386,7 @@
 
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .setUserId(TEST_USER_1_ID)
                 .build();
         assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType());
@@ -399,7 +401,7 @@
     public void testAddTaskCompatibleWindowingMode_expectRemove() {
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .build();
         setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED);
         assertEquals(WINDOWING_MODE_UNDEFINED, task1.getWindowingMode());
@@ -408,7 +410,7 @@
 
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .build();
         setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN);
         assertEquals(WINDOWING_MODE_FULLSCREEN, task2.getWindowingMode());
@@ -425,7 +427,7 @@
     public void testAddTaskIncompatibleWindowingMode_expectNoRemove() {
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .build();
         setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN);
         assertEquals(WINDOWING_MODE_FULLSCREEN, task1.getWindowingMode());
@@ -433,7 +435,7 @@
 
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .build();
         setTaskWindowingMode(task2, WINDOWING_MODE_PINNED);
         assertEquals(WINDOWING_MODE_PINNED, task2.getWindowingMode());
@@ -447,6 +449,31 @@
     }
 
     @Test
+    public void testRemoveAffinityTask() {
+        // Add task to recents
+        final String taskAffinity = "affinity";
+        final int uid = 10123;
+        final Task task1 = createTaskBuilder(".Task1").setParentTask(mStack).build();
+        task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+        mRecentTasks.add(task1);
+
+        // Add another task to recents, and make sure the previous task was removed.
+        final Task task2 = createTaskBuilder(".Task2").setParentTask(mStack).build();
+        task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+        mRecentTasks.add(task2);
+        assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+                true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+
+        // Add another single-instance task to recents, and make sure no task is removed.
+        final Task task3 = createTaskBuilder(".Task3").setParentTask(mStack).build();
+        task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid,
+                LAUNCH_SINGLE_INSTANCE);
+        mRecentTasks.add(task3);
+        assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+                true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+    }
+
+    @Test
     public void testAddTasksHomeClearUntrackedTasks_expectFinish() {
         // There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK |
         // FLAG_ACTIVITY_MULTIPLE_TASK). If the previous task is still active, it should be removed
@@ -470,7 +497,7 @@
         // tasks because their intents are identical.
         mRecentTasks.add(task1);
         // Go home to trigger the removal of untracked tasks.
-        mRecentTasks.add(createTaskBuilder(".Home").setStack(mTaskContainer.getRootHomeTask())
+        mRecentTasks.add(createTaskBuilder(".Home").setParentTask(mTaskContainer.getRootHomeTask())
                 .build());
 
         // The task was added into recents again so it is not hidden and shouldn't be removed.
@@ -856,10 +883,10 @@
 
         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
         // the tasks belong in stacks above the home stack
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setStack(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task3").setStack(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task3").setParentTask(aboveHomeStack).build());
 
         assertNoTasksTrimmed();
     }
@@ -877,11 +904,11 @@
         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
         // the home stack is trimmed once a new task is added
         final Task behindHomeTask = createTaskBuilder(".Task1")
-                .setStack(behindHomeStack)
+                .setParentTask(behindHomeStack)
                 .build();
         mRecentTasks.add(behindHomeTask);
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
 
         assertTrimmed(behindHomeTask);
     }
@@ -897,10 +924,10 @@
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setStack(otherDisplayStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setStack(otherDisplayStack).build());
-        mRecentTasks.add(createTaskBuilder(".HomeTask2").setStack(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeStack).build());
 
         assertNoTasksTrimmed();
     }
@@ -1187,7 +1214,7 @@
     private TaskBuilder createTaskBuilder(String packageName, String className) {
         return new TaskBuilder(mAtm.mStackSupervisor)
                 .setComponent(new ComponentName(packageName, className))
-                .setStack(mStack)
+                .setParentTask(mStack)
                 .setUserId(TEST_USER_0_ID);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index b89d168..901ed36 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -195,8 +195,7 @@
     public void testApplySleepTokens() {
         final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
         final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final Task stack = new StackBuilder(mRootWindowContainer)
-                .setCreateActivity(false)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setDisplay(display)
                 .setOnTop(false)
                 .build();
@@ -384,7 +383,7 @@
         final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea()
                 .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
                         true /* onTop */);
-        final Task task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryStack).build();
         final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build();
 
         // Find a launch stack for the top activity in split-screen primary, while requesting
@@ -404,14 +403,17 @@
     @Test
     public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
         // Create stack/task on default display.
-        final Task targetStack = new StackBuilder(mRootWindowContainer)
+        final Task targetStack = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
                 .setOnTop(false)
                 .build();
         final Task targetTask = targetStack.getBottomMostTask();
 
         // Create Recents on top of the display.
-        final Task stack = new StackBuilder(mRootWindowContainer).setActivityType(
-                ACTIVITY_TYPE_RECENTS).build();
+        final Task stack = new TaskBuilder(mSupervisor)
+                .setCreateActivity(true)
+                .setActivityType(ACTIVITY_TYPE_RECENTS)
+                .build();
 
         final String reason = "findTaskToMoveToFront";
         mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
@@ -431,14 +433,14 @@
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task targetStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
 
         // Create Recents on secondary display.
         final TestDisplayContent secondDisplay = addNewDisplayContentAt(
                 DisplayContent.POSITION_TOP);
         final Task stack = secondDisplay.getDefaultTaskDisplayArea()
                 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         new ActivityBuilder(mAtm).setTask(task).build();
 
         final String reason = "findTaskToMoveToFront";
@@ -458,7 +460,7 @@
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
         taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
 
@@ -514,7 +516,7 @@
                 DisplayContent.POSITION_TOP);
         final Task stack = secondDisplay.getDefaultTaskDisplayArea()
                 .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         new ActivityBuilder(mAtm).setTask(task).build();
 
         doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
@@ -538,7 +540,7 @@
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
         activity.setState(ActivityState.RESUMED, "test");
 
@@ -558,7 +560,7 @@
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
         activity.setState(ActivityState.RESUMED, "test");
         taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
@@ -884,7 +886,7 @@
         // Create a root task with an activity on secondary display.
         final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300,
                 600).build();
-        final Task task = new StackBuilder(mRootWindowContainer)
+        final Task task = new TaskBuilder(mSupervisor)
                 .setDisplay(secondaryDisplay).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
 
@@ -915,24 +917,6 @@
         assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask());
     }
 
-    @Test
-    public void testResumeFocusedStackOnSleepingDisplay() {
-        // Create an activity on secondary display.
-        final TestDisplayContent secondDisplay = addNewDisplayContentAt(
-                DisplayContent.POSITION_TOP);
-        final Task stack = secondDisplay.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setStack(stack).build();
-        spyOn(activity);
-        spyOn(stack);
-
-        // Cannot resumed activities on secondary display if the display should sleep.
-        doReturn(true).when(secondDisplay).shouldSleep();
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
-        verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any());
-        verify(activity, never()).makeActiveIfNeeded(any());
-    }
-
     /**
      * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
      * info for test cases.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 191c33c..3053fe6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -174,7 +174,7 @@
 
     @Test
     public void testForceStopPackage() {
-        final Task task = new StackBuilder(mWm.mRoot).build();
+        final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         final ActivityRecord activity = task.getTopMostActivity();
         final WindowProcessController wpc = activity.app;
         final ActivityRecord[] activities = {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 3415093..8a5b13c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -62,8 +62,7 @@
 
         final int numStacks = 2;
         for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
-            final Task stack = new StackBuilder(mRootWindowContainer)
-                    .setCreateActivity(false)
+            final Task stack = new TaskBuilder(mSupervisor)
                     .setDisplay(display)
                     .setOnTop(false)
                     .build();
@@ -104,8 +103,7 @@
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
         final int numTasks = 10;
         for (int i = 0; i < numTasks; i++) {
-            final Task stack = new StackBuilder(mRootWindowContainer)
-                    .setCreateActivity(false)
+            final Task stack = new TaskBuilder(mSupervisor)
                     .setDisplay(display)
                     .setOnTop(true)
                     .build();
@@ -135,7 +133,7 @@
         final Task task = new TaskBuilder(mAtm.mStackSupervisor)
                 .setComponent(new ComponentName(mContext.getPackageName(), className))
                 .setTaskId(taskId)
-                .setStack(stack)
+                .setParentTask(stack)
                 .build();
         task.lastActiveTime = lastActiveTime;
         final ActivityRecord activity = new ActivityBuilder(mAtm)
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 982e469..29081d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -35,7 +35,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -70,7 +72,7 @@
     private ActivityRecord mActivity;
 
     private void setUpApp(DisplayContent display) {
-        mStack = new StackBuilder(mRootWindowContainer).setDisplay(display).build();
+        mStack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
         mTask = mStack.getBottomMostTask();
         mActivity = mTask.getTopNonFinishingActivity();
     }
@@ -90,7 +92,7 @@
         prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
 
         final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
-        resizeDisplay(mStack.getDisplay(), 600, 1200);
+        resizeDisplay(mStack.mDisplayContent, 600, 1200);
         // The visible activity should recompute configuration according to the last parent bounds.
         mAtm.restartActivityProcessIfVisible(mActivity.appToken);
 
@@ -216,22 +218,50 @@
 
         final Rect origBounds = new Rect(mActivity.getBounds());
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+        final DisplayContent display = mActivity.mDisplayContent;
 
         // Change the size of current display.
-        resizeDisplay(mStack.getDisplay(), 1000, 2000);
-
+        resizeDisplay(display, 1000, 2000);
+        // The bounds should be [100, 0 - 1100, 2500].
         assertEquals(origBounds.width(), currentBounds.width());
         assertEquals(origBounds.height(), currentBounds.height());
         assertScaled();
 
+        // The scale is 2000/2500=0.8. The horizontal centered offset is (1000-(1000*0.8))/2=100.
+        final float scale = (float) display.mBaseDisplayHeight / currentBounds.height();
+        final int offsetX = (int) (display.mBaseDisplayWidth - (origBounds.width() * scale)) / 2;
+        assertEquals(offsetX, currentBounds.left);
+
         // The position of configuration bounds should be the same as compat bounds.
         assertEquals(mActivity.getBounds().left, currentBounds.left);
         assertEquals(mActivity.getBounds().top, currentBounds.top);
 
         // Change display size to a different orientation
-        resizeDisplay(mStack.getDisplay(), 2000, 1000);
+        resizeDisplay(display, 2000, 1000);
+        // The bounds should be [800, 0 - 1800, 2500].
         assertEquals(origBounds.width(), currentBounds.width());
         assertEquals(origBounds.height(), currentBounds.height());
+        assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
+
+        // The previous resize operation doesn't consider the rotation change after size changed.
+        // These setups apply the requested orientation to rotation as real case that the top fixed
+        // portrait activity will determine the display rotation.
+        final DisplayRotation displayRotation = display.getDisplayRotation();
+        doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean());
+        // Skip unrelated layout procedures.
+        mAtm.deferWindowLayout();
+        display.reconfigureDisplayLocked();
+        displayRotation.updateOrientation(display.getOrientation(), true /* forceUpdate */);
+        display.sendNewConfiguration();
+
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, display.getConfiguration().orientation);
+        assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
+        // The size should still be in portrait [100, 0 - 1100, 2500] = 1000x2500.
+        assertEquals(origBounds.width(), currentBounds.width());
+        assertEquals(origBounds.height(), currentBounds.height());
+        assertEquals(offsetX, currentBounds.left);
+        assertScaled();
     }
 
     @Test
@@ -412,7 +442,7 @@
     public void testResetNonVisibleActivity() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
-        final DisplayContent display = mStack.getDisplay();
+        final DisplayContent display = mStack.mDisplayContent;
         // Resize the display so the activity is in size compatibility mode.
         resizeDisplay(display, 900, 1800);
 
@@ -464,7 +494,7 @@
                 });
 
         // Resize the display so that the activity exercises size-compat mode.
-        resizeDisplay(mStack.getDisplay(), 1000, 2500);
+        resizeDisplay(mStack.mDisplayContent, 1000, 2500);
 
         // Expect the exact token when the activity is in size compatibility mode.
         assertEquals(1, compatTokens.size());
@@ -477,7 +507,7 @@
         activity.restartProcessIfVisible();
         // The full lifecycle isn't hooked up so manually set state to resumed
         activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
-        mStack.getDisplay().handleActivitySizeCompatModeIfNeeded(activity);
+        mStack.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
 
         // Expect null token when switching to non-size-compat mode activity.
         assertEquals(1, compatTokens.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 260f1e9..bc3b3a4d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -218,7 +218,7 @@
         final Task rootHomeTask = defaultTaskDisplayArea.getRootHomeTask();
         rootHomeTask.mResizeMode = RESIZE_MODE_UNRESIZEABLE;
 
-        final Task primarySplitTask = new StackBuilder(rootWindowContainer)
+        final Task primarySplitTask = new TaskBuilder(mSupervisor)
                 .setTaskDisplayArea(defaultTaskDisplayArea)
                 .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 42de5e6..1d32e17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1358,7 +1358,7 @@
         final Task stack = display.getDefaultTaskDisplayArea()
                 .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
         stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         // Just work around the unnecessary adjustments for bounds.
         task.getWindowConfiguration().setBounds(bounds);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 08537a4..a908bfe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -176,7 +176,7 @@
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
         Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         final Configuration parentConfig = stack.getConfiguration();
         parentConfig.windowConfiguration.setBounds(parentBounds);
         parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
@@ -212,7 +212,7 @@
     @Test
     public void testBoundsOnModeChangeFreeformToFullscreen() {
         DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
-        Task stack = new StackBuilder(mRootWindowContainer).setDisplay(display)
+        Task stack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         Task task = stack.getBottomMostTask();
         task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
@@ -253,7 +253,7 @@
         dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
         dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
 
-        final Task stack = new StackBuilder(mRootWindowContainer)
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
         final Task task = stack.getBottomMostTask();
         final ActivityRecord root = task.getTopNonFinishingActivity();
@@ -317,7 +317,7 @@
         dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
         dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
 
-        final Task stack = new StackBuilder(mRootWindowContainer)
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
         final Task task = stack.getBottomMostTask();
         ActivityRecord root = task.getTopNonFinishingActivity();
@@ -341,7 +341,7 @@
                 Configuration.ORIENTATION_LANDSCAPE;
         display.onRequestedOverrideConfigurationChanged(
                 display.getRequestedOverrideConfiguration());
-        Task stack = new StackBuilder(mRootWindowContainer)
+        Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
         Task task = stack.getBottomMostTask();
         ActivityRecord root = task.getTopNonFinishingActivity();
@@ -497,7 +497,7 @@
         DisplayInfo displayInfo = new DisplayInfo();
         mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
         final int displayHeight = displayInfo.logicalHeight;
-        final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
         final Configuration inOutConfig = new Configuration();
         final Configuration parentConfig = new Configuration();
         final int longSide = 1200;
@@ -1029,7 +1029,7 @@
     }
 
     private Task getTestTask() {
-        final Task stack = new StackBuilder(mRootWindowContainer).build();
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
         return stack.getBottomMostTask();
     }
 
@@ -1039,7 +1039,7 @@
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
         Task stack = taskDisplayArea.createStack(windowingMode, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build();
 
         final Configuration parentConfig = stack.getConfiguration();
         parentConfig.windowConfiguration.setAppBounds(parentBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 4163a9a..36f3a21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -829,7 +829,7 @@
         final DisplayContent displayContent = createNewDisplay();
         // Do not reparent activity to default display when removing the display.
         doReturn(true).when(displayContent).shouldDestroyContentOnRemove();
-        final ActivityRecord r = new StackBuilder(mWm.mRoot)
+        final ActivityRecord r = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setDisplay(displayContent).build().getTopMostActivity();
         // Add a window and make the activity animating so the removal of activity is deferred.
         createWindow(null, TYPE_BASE_APPLICATION, r, "win");
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index f97dff3..2510385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -114,11 +114,11 @@
         Task tappedStack = createTaskStackOnDisplay(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, display);
         Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
-        spyOn(mWm.mActivityTaskManager);
+        spyOn(mWm.mAtmService);
 
         mWm.handleTaskFocusChange(tappedTask);
 
-        verify(mWm.mActivityTaskManager).setFocusedTask(tappedTask.mTaskId);
+        verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
     }
 
     @Test
@@ -135,11 +135,11 @@
         Task tappedStack = createTaskStackOnDisplay(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, display);
         Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
-        spyOn(mWm.mActivityTaskManager);
+        spyOn(mWm.mAtmService);
 
         mWm.handleTaskFocusChange(tappedTask);
 
-        verify(mWm.mActivityTaskManager, never()).setFocusedTask(tappedTask.mTaskId);
+        verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId);
     }
 
     @Test
@@ -158,10 +158,10 @@
         Task tappedStack = createTaskStackOnTaskDisplayArea(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, secondTda);
         Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */);
-        spyOn(mWm.mActivityTaskManager);
+        spyOn(mWm.mAtmService);
 
         mWm.handleTaskFocusChange(tappedTask);
 
-        verify(mWm.mActivityTaskManager).setFocusedTask(tappedTask.mTaskId);
+        verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 289d54e..38909f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -290,7 +290,7 @@
     @Test
     public void testTaskTransaction() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new StackBuilder(mWm.mRoot)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         final Task task = stack.getTopMostTask();
         testTransaction(task);
@@ -299,7 +299,7 @@
     @Test
     public void testStackTransaction() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new StackBuilder(mWm.mRoot)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         StackInfo info =
                 mWm.mAtmService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
@@ -324,7 +324,7 @@
 
     @Test
     public void testSetWindowingMode() {
-        final Task stack = new StackBuilder(mWm.mRoot)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         testSetWindowingMode(stack);
 
@@ -358,7 +358,7 @@
     @Test
     public void testContainerFocusableChanges() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new StackBuilder(mWm.mRoot)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         final Task task = stack.getTopMostTask();
         WindowContainerTransaction t = new WindowContainerTransaction();
@@ -374,7 +374,7 @@
     @Test
     public void testContainerHiddenChanges() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new StackBuilder(mWm.mRoot)
+        final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         WindowContainerTransaction t = new WindowContainerTransaction();
         assertTrue(stack.shouldBeVisible(null));
@@ -389,7 +389,7 @@
     @Test
     public void testOverrideConfigSize() {
         removeGlobalMinSizeRestriction();
-        final Task stack = new StackBuilder(mWm.mRoot)
+        final Task stack = new TaskBuilder(mSupervisor)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
         final Task task = stack.getTopMostTask();
         WindowContainerTransaction t = new WindowContainerTransaction();
@@ -930,23 +930,36 @@
         final Task stack = createStack();
         final Task task = createTask(stack);
         final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task);
+        final Task stack2 = createStack();
+        final Task task2 = createTask(stack2);
+        final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         // Setup the task to be controlled by the MW mode organizer
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         assertTrue(stack.isOrganized());
+        assertTrue(stack2.isOrganized());
 
         // Verify a back pressed does not call the organizer
         mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
         verify(organizer, never()).onBackPressedOnTaskRoot(any());
 
         // Enable intercepting back
-        mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(organizer,
-                true);
+        mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
+                stack.mRemoteToken.toWindowContainerToken(), true);
 
         // Verify now that the back press does call the organizer
         mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
         verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
+
+        // Disable intercepting back
+        mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(
+                stack.mRemoteToken.toWindowContainerToken(), false);
+
+        // Verify now that the back press no longer calls the organizer
+        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
+        verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 9603d28..3106ca2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -661,14 +661,14 @@
         RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
         when(recentsController.shouldApplyInputConsumer(win0.mActivityRecord)).thenReturn(true);
         mWm.setRecentsAnimationController(recentsController);
-        assertTrue(win0.cantReceiveTouchInput());
+        assertFalse(win0.canReceiveTouchInput());
     }
 
     @Test
     public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
         final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
         win0.mActivityRecord.mVisibleRequested = false;
-        assertTrue(win0.cantReceiveTouchInput());
+        assertFalse(win0.canReceiveTouchInput());
     }
 
     @Test
@@ -676,7 +676,7 @@
         final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
         win0.mActivityRecord.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         win0.mActivityRecord.getStack().setFocusable(false);
-        assertTrue(win0.cantReceiveTouchInput());
+        assertFalse(win0.canReceiveTouchInput());
     }
 
     @UseTestDisplay(addWindows = W_ACTIVITY)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 38c4e0a..7daddd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -90,6 +90,7 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.runner.Description;
+import org.mockito.Mockito;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -398,22 +399,20 @@
     }
 
     Task createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
-        return new StackBuilder(dc.mWmService.mRoot)
+        return new TaskBuilder(dc.mAtmService.mStackSupervisor)
                 .setDisplay(dc)
                 .setWindowingMode(windowingMode)
                 .setActivityType(activityType)
-                .setCreateActivity(false)
                 .setIntent(new Intent())
                 .build();
     }
 
     Task createTaskStackOnTaskDisplayArea(int windowingMode, int activityType,
             TaskDisplayArea tda) {
-        return new StackBuilder(tda.mWmService.mRoot)
+        return new TaskBuilder(tda.mDisplayContent.mAtmService.mStackSupervisor)
                 .setTaskDisplayArea(tda)
                 .setWindowingMode(windowingMode)
                 .setActivityType(activityType)
-                .setCreateActivity(false)
                 .setIntent(new Intent())
                 .build();
     }
@@ -422,7 +421,7 @@
     Task createTaskInStack(Task stack, int userId) {
         final Task task = new TaskBuilder(stack.mStackSupervisor)
                 .setUserId(userId)
-                .setStack(stack)
+                .setParentTask(stack)
                 .build();
         return task;
     }
@@ -733,7 +732,7 @@
             if (mCreateTask) {
                 mTask = new TaskBuilder(mService.mStackSupervisor)
                         .setComponent(mComponent)
-                        .setStack(mStack).build();
+                        .setParentTask(mStack).build();
             } else if (mTask == null && mStack != null && DisplayContent.alwaysCreateStack(
                     mStack.getWindowingMode(), mStack.getActivityType())) {
                 // The stack can be the task root.
@@ -813,20 +812,42 @@
     protected static class TaskBuilder {
         private final ActivityStackSupervisor mSupervisor;
 
+        private TaskDisplayArea mTaskDisplayArea;
         private ComponentName mComponent;
         private String mPackage;
         private int mFlags = 0;
-        // Task id 0 is reserved in ARC for the home app.
-        private int mTaskId = SystemServicesTestRule.sNextTaskId++;
+        private int mTaskId = -1;
         private int mUserId = 0;
+        private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+        private int mActivityType = ACTIVITY_TYPE_STANDARD;
+        private ActivityInfo mActivityInfo;
+        private Intent mIntent;
+        private boolean mOnTop = true;
         private IVoiceInteractionSession mVoiceSession;
-        private boolean mCreateStack = true;
 
-        private Task mStack;
-        private TaskDisplayArea mTaskDisplayArea;
+        private boolean mCreateParentTask = false;
+        private Task mParentTask;
+
+        private boolean mCreateActivity = false;
 
         TaskBuilder(ActivityStackSupervisor supervisor) {
             mSupervisor = supervisor;
+            mTaskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+        }
+
+        /**
+         * Set the parent {@link DisplayContent} and use the default task display area. Overrides
+         * the task display area, if was set before.
+         */
+        TaskBuilder setDisplay(DisplayContent display) {
+            mTaskDisplayArea = display.getDefaultTaskDisplayArea();
+            return this;
+        }
+
+        /** Set the parent {@link TaskDisplayArea}. Overrides the display, if was set before. */
+        TaskBuilder setTaskDisplayArea(TaskDisplayArea taskDisplayArea) {
+            mTaskDisplayArea = taskDisplayArea;
+            return this;
         }
 
         TaskBuilder setComponent(ComponentName component) {
@@ -839,20 +860,6 @@
             return this;
         }
 
-        /**
-         * Set to {@code true} by default, set to {@code false} to prevent the task from
-         * automatically creating a parent stack.
-         */
-        TaskBuilder setCreateStack(boolean createStack) {
-            mCreateStack = createStack;
-            return this;
-        }
-
-        TaskBuilder setVoiceSession(IVoiceInteractionSession session) {
-            mVoiceSession = session;
-            return this;
-        }
-
         TaskBuilder setFlags(int flags) {
             mFlags = flags;
             return this;
@@ -868,158 +875,119 @@
             return this;
         }
 
-        TaskBuilder setStack(Task stack) {
-            mStack = stack;
+        TaskBuilder setWindowingMode(int windowingMode) {
+            mWindowingMode = windowingMode;
             return this;
         }
 
-        TaskBuilder setDisplay(DisplayContent display) {
-            mTaskDisplayArea = display.getDefaultTaskDisplayArea();
+        TaskBuilder setActivityType(int activityType) {
+            mActivityType = activityType;
+            return this;
+        }
+
+        TaskBuilder setActivityInfo(ActivityInfo info) {
+            mActivityInfo = info;
+            return this;
+        }
+
+        TaskBuilder setIntent(Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        TaskBuilder setOnTop(boolean onTop) {
+            mOnTop = onTop;
+            return this;
+        }
+
+        TaskBuilder setVoiceSession(IVoiceInteractionSession session) {
+            mVoiceSession = session;
+            return this;
+        }
+
+        TaskBuilder setCreateParentTask(boolean createParentTask) {
+            mCreateParentTask = createParentTask;
+            return this;
+        }
+
+        TaskBuilder setParentTask(Task parentTask) {
+            mParentTask = parentTask;
+            return this;
+        }
+
+        TaskBuilder setCreateActivity(boolean createActivity) {
+            mCreateActivity = createActivity;
             return this;
         }
 
         Task build() {
             SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock);
 
-            if (mStack == null && mCreateStack) {
-                TaskDisplayArea displayArea = mTaskDisplayArea != null ? mTaskDisplayArea
-                        : mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
-                mStack = displayArea.createStack(
+            // Create parent task.
+            if (mParentTask == null && mCreateParentTask) {
+                mParentTask = mTaskDisplayArea.createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-                spyOn(mStack);
+            }
+            if (mParentTask != null && !Mockito.mockingDetails(mParentTask).isSpy()) {
+                spyOn(mParentTask);
             }
 
-            final ActivityInfo aInfo = new ActivityInfo();
-            aInfo.applicationInfo = new ApplicationInfo();
-            aInfo.applicationInfo.packageName = mPackage;
-
-            Intent intent = new Intent();
-            if (mComponent == null) {
-                mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
-                        DEFAULT_COMPONENT_CLASS_NAME);
+            // Create task.
+            if (mActivityInfo == null) {
+                mActivityInfo = new ActivityInfo();
+                mActivityInfo.applicationInfo = new ApplicationInfo();
+                mActivityInfo.applicationInfo.packageName = mPackage;
             }
 
-            intent.setComponent(mComponent);
-            intent.setFlags(mFlags);
+            if (mIntent == null) {
+                mIntent = new Intent();
+                if (mComponent == null) {
+                    mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
+                            DEFAULT_COMPONENT_CLASS_NAME);
+                }
+                mIntent.setComponent(mComponent);
+                mIntent.setFlags(mFlags);
+            }
 
-            final Task task = new Task(mSupervisor.mService, mTaskId, aInfo,
-                    intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/,
-                    null /*taskDescription*/, mStack);
+            Task task;
+            final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextStackId();
+            if (mParentTask == null) {
+                task = mTaskDisplayArea.createStackUnchecked(
+                        mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo,
+                        mIntent, false /* createdByOrganizer */);
+            } else {
+                task = new Task(mSupervisor.mService, taskId, mActivityInfo,
+                        mIntent /*intent*/, mVoiceSession, null /*_voiceInteractor*/,
+                        null /*taskDescription*/, mParentTask);
+                mParentTask.moveToFront("build-task");
+                mParentTask.addChild(task, true, true);
+            }
             spyOn(task);
             task.mUserId = mUserId;
+            Task rootTask = task.getRootTask();
+            doNothing().when(rootTask).startActivityLocked(
+                    any(), any(), anyBoolean(), anyBoolean(), any());
 
-            if (mStack != null) {
-                mStack.moveToFront("test");
-                mStack.addChild(task, true, true);
+            // Create child task with activity.
+            if (mCreateActivity) {
+                new ActivityBuilder(mSupervisor.mService)
+                        .setCreateTask(true)
+                        .setStack(task)
+                        .build();
+                if (mOnTop) {
+                    // We move the task to front again in order to regain focus after activity
+                    // added to the stack. Or {@link TaskDisplayArea#mPreferredTopFocusableStack}
+                    // could be other stacks (e.g. home stack).
+                    task.moveToFront("createActivityTask");
+                } else {
+                    task.moveToBack("createActivityTask", null);
+                }
             }
 
             return task;
         }
     }
 
-    static class StackBuilder {
-        private final RootWindowContainer mRootWindowContainer;
-        private DisplayContent mDisplay;
-        private TaskDisplayArea mTaskDisplayArea;
-        private int mStackId = -1;
-        private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
-        private int mActivityType = ACTIVITY_TYPE_STANDARD;
-        private boolean mOnTop = true;
-        private boolean mCreateActivity = true;
-        private ActivityInfo mInfo;
-        private Intent mIntent;
-
-        StackBuilder(RootWindowContainer root) {
-            mRootWindowContainer = root;
-            mDisplay = mRootWindowContainer.getDefaultDisplay();
-            mTaskDisplayArea = mDisplay.getDefaultTaskDisplayArea();
-        }
-
-        StackBuilder setWindowingMode(int windowingMode) {
-            mWindowingMode = windowingMode;
-            return this;
-        }
-
-        StackBuilder setActivityType(int activityType) {
-            mActivityType = activityType;
-            return this;
-        }
-
-        StackBuilder setStackId(int stackId) {
-            mStackId = stackId;
-            return this;
-        }
-
-        /**
-         * Set the parent {@link DisplayContent} and use the default task display area. Overrides
-         * the task display area, if was set before.
-         */
-        StackBuilder setDisplay(DisplayContent display) {
-            mDisplay = display;
-            mTaskDisplayArea = mDisplay.getDefaultTaskDisplayArea();
-            return this;
-        }
-
-        /** Set the parent {@link TaskDisplayArea}. Overrides the display, if was set before. */
-        StackBuilder setTaskDisplayArea(TaskDisplayArea taskDisplayArea) {
-            mTaskDisplayArea = taskDisplayArea;
-            mDisplay = mTaskDisplayArea.mDisplayContent;
-            return this;
-        }
-
-        StackBuilder setOnTop(boolean onTop) {
-            mOnTop = onTop;
-            return this;
-        }
-
-        StackBuilder setCreateActivity(boolean createActivity) {
-            mCreateActivity = createActivity;
-            return this;
-        }
-
-        StackBuilder setActivityInfo(ActivityInfo info) {
-            mInfo = info;
-            return this;
-        }
-
-        StackBuilder setIntent(Intent intent) {
-            mIntent = intent;
-            return this;
-        }
-
-        Task build() {
-            SystemServicesTestRule.checkHoldsLock(mRootWindowContainer.mWmService.mGlobalLock);
-
-            final int stackId = mStackId >= 0 ? mStackId : mTaskDisplayArea.getNextStackId();
-            final Task stack = mTaskDisplayArea.createStackUnchecked(
-                    mWindowingMode, mActivityType, stackId, mOnTop, mInfo, mIntent,
-                    false /* createdByOrganizer */);
-            final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor;
-
-            if (mCreateActivity) {
-                new ActivityBuilder(supervisor.mService)
-                        .setCreateTask(true)
-                        .setStack(stack)
-                        .build();
-                if (mOnTop) {
-                    // We move the task to front again in order to regain focus after activity
-                    // added to the stack. Or {@link DisplayContent#mPreferredTopFocusableStack}
-                    // could be other stacks (e.g. home stack).
-                    stack.moveToFront("createActivityStack");
-                } else {
-                    stack.moveToBack("createActivityStack", null);
-                }
-            }
-            spyOn(stack);
-
-            doNothing().when(stack).startActivityLocked(
-                    any(), any(), anyBoolean(), anyBoolean(), any());
-
-            return stack;
-        }
-
-    }
-
     static class TestSplitOrganizer extends ITaskOrganizer.Stub {
         final ActivityTaskManagerService mService;
         Task mPrimary;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 81aad97..f151d9c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -183,6 +183,7 @@
     private static class ActivityData {
         private final String mTaskRootPackage;
         private final String mTaskRootClass;
+        public int lastEvent = Event.NONE;
         private ActivityData(String taskRootPackage, String taskRootClass) {
             mTaskRootPackage = taskRootPackage;
             mTaskRootClass = taskRootClass;
@@ -785,6 +786,7 @@
         switch (event.mEventType) {
             case Event.ACTIVITY_RESUMED:
             case Event.ACTIVITY_PAUSED:
+            case Event.ACTIVITY_STOPPED:
                 uid = mPackageManagerInternal.getPackageUid(event.mPackage, 0, userId);
                 break;
             default:
@@ -817,8 +819,10 @@
                                     .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND);
                     // check if this activity has already been resumed
                     if (mVisibleActivities.get(event.mInstanceId) != null) break;
-                    mVisibleActivities.put(event.mInstanceId,
-                            new ActivityData(event.mTaskRootPackage, event.mTaskRootClass));
+                    final ActivityData resumedData = new ActivityData(event.mTaskRootPackage,
+                            event.mTaskRootClass);
+                    resumedData.lastEvent = Event.ACTIVITY_RESUMED;
+                    mVisibleActivities.put(event.mInstanceId, resumedData);
                     try {
                         switch(mUsageSource) {
                             case USAGE_SOURCE_CURRENT_ACTIVITY:
@@ -834,16 +838,17 @@
                     }
                     break;
                 case Event.ACTIVITY_PAUSED:
-                    if (event.mTaskRootPackage == null) {
-                        // Task Root info is missing. Repair the event based on previous data
-                        final ActivityData prevData = mVisibleActivities.get(event.mInstanceId);
-                        if (prevData == null) {
-                            Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage
-                                    + "/" + event.mClass + " event : " + event.mEventType
-                                    + " instanceId : " + event.mInstanceId + ")");
-                        } else {
-                            event.mTaskRootPackage = prevData.mTaskRootPackage;
-                            event.mTaskRootClass = prevData.mTaskRootClass;
+                    final ActivityData pausedData = mVisibleActivities.get(event.mInstanceId);
+                    if (pausedData == null) {
+                        Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage
+                                + "/" + event.mClass + " event : " + event.mEventType
+                                + " instanceId : " + event.mInstanceId + ")");
+                    } else {
+                        pausedData.lastEvent = Event.ACTIVITY_PAUSED;
+                        if (event.mTaskRootPackage == null) {
+                            // Task Root info is missing. Repair the event based on previous data
+                            event.mTaskRootPackage = pausedData.mTaskRootPackage;
+                            event.mTaskRootClass = pausedData.mTaskRootClass;
                         }
                     }
                     FrameworkStatsLog.write(
@@ -866,6 +871,16 @@
                         return;
                     }
 
+                    if (prevData.lastEvent != Event.ACTIVITY_PAUSED) {
+                        FrameworkStatsLog.write(
+                                FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED,
+                                uid,
+                                event.mPackage,
+                                event.mClass,
+                                FrameworkStatsLog
+                                        .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND);
+                    }
+
                     ArraySet<String> tokens;
                     synchronized (mUsageReporters) {
                         tokens = mUsageReporters.removeReturnOld(event.mInstanceId);
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
index 673a0a9..9969ee9 100644
--- a/telecomm/OWNERS
+++ b/telecomm/OWNERS
@@ -1,7 +1,8 @@
 set noparent
 
-tgunn@google.com
 breadley@google.com
 hallliu@google.com
+tgunn@google.com
+xiaotonj@google.com
+shuoq@google.com
 rgreenwalt@google.com
-paulye@google.com
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 67e5eab..9d17219 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -17,6 +17,7 @@
 package android.telecom.Logging;
 
 import android.annotation.Nullable;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -453,7 +454,9 @@
      * perform a sweep to check and make sure that the session is still not incomplete (stale).
      */
     private long getCleanupTimeout(Context context) {
-        return Settings.Secure.getLong(context.getContentResolver(), TIMEOUTS_PREFIX +
-                "stale_session_cleanup_timeout_millis", DEFAULT_SESSION_TIMEOUT_MS);
+        final ContentResolver cr = context.getContentResolver();
+        return Settings.Secure.getLongForUser(cr, TIMEOUTS_PREFIX
+                        + "stale_session_cleanup_timeout_millis", DEFAULT_SESSION_TIMEOUT_MS,
+                cr.getUserId());
     }
 }
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 944edd5..72e11c8 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -291,6 +291,22 @@
     method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String);
   }
 
+  public final class PhysicalChannelConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCellBandwidthDownlink();
+    method public int getChannelNumber();
+    method public int getConnectionStatus();
+    method public int getNetworkType();
+    method @IntRange(from=0, to=1007) public int getPhysicalCellId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
+    field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+    field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+    field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
+    field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
+  }
+
   public final class PreciseCallState implements android.os.Parcelable {
     ctor public PreciseCallState(int, int, int, int, int);
     method public int describeContents();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a229efb..470d4be 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1678,6 +1678,15 @@
             "hide_lte_plus_data_icon_bool";
 
     /**
+     * The combined channel bandwidth threshold (non-inclusive) in KHz required to display the
+     * LTE+ data icon. It is 20000 by default, meaning the LTE+ icon will be shown if the device is
+     * using carrier aggregation and the combined channel bandwidth is strictly greater than 20 MHz.
+     * @hide
+     */
+    public static final String KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT =
+            "lte_plus_threshold_bandwidth_khz_int";
+
+    /**
      * The string is used to filter redundant string from PLMN Network Name that's supplied by
      * specific carrier.
      *
@@ -4259,6 +4268,7 @@
         sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, "");
         sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
         sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
+        sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
         sDefaults.putBoolean(KEY_NR_ENABLED_BOOL, true);
         sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false);
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index af62ba4..8d49e15 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -17,6 +17,9 @@
 package android.telephony;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Annotation.NetworkType;
@@ -29,9 +32,11 @@
 /**
  * @hide
  */
+@SystemApi
 public final class PhysicalChannelConfig implements Parcelable {
 
     // TODO(b/72993578) consolidate these enums in a central location.
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING, CONNECTION_UNKNOWN})
     public @interface ConnectionStatus {}
@@ -47,7 +52,13 @@
     public static final int CONNECTION_SECONDARY_SERVING = 2;
 
     /** Connection status is unknown. */
-    public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
+    public static final int CONNECTION_UNKNOWN = -1;
+
+    /** Channel number is unknown. */
+    public static final int CHANNEL_NUMBER_UNKNOWN = -1;
+
+    /** Physical Cell Id is unknown. */
+    public static final int PHYSICAL_CELL_ID_UNKNOWN = -1;
 
     /**
      * Connection status of the cell.
@@ -75,7 +86,7 @@
     private int mFrequencyRange;
 
     /**
-     * The absolute radio frequency channel number, {@link Integer#MAX_VALUE} if unknown.
+     * The absolute radio frequency channel number, {@link CHANNEL_NUMBER_UNKNOWN} if unknown.
      */
     private int mChannelNumber;
 
@@ -86,7 +97,8 @@
     private int[] mContextIds;
 
     /**
-     * The physical cell identifier for this cell - PCI, PSC, {@link Integer#MAX_VALUE} if known.
+     * The physical cell identifier for this cell - PCI, PSC, {@link PHYSICAL_CELL_ID_UNKNOWN}
+     * if unknown.
      */
     private int mPhysicalCellId;
 
@@ -96,7 +108,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mCellConnectionStatus);
         dest.writeInt(mCellBandwidthDownlinkKhz);
         dest.writeInt(mRat);
@@ -120,6 +132,7 @@
      * physical channel has no data call mapped to it.
      *
      * @return an integer list indicates the data call ids.
+     * @hide
      */
     public int[] getContextIds() {
         return mContextIds;
@@ -131,6 +144,7 @@
      * @see {@link ServiceState#FREQUENCY_RANGE_MID}
      * @see {@link ServiceState#FREQUENCY_RANGE_HIGH}
      * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE}
+     * @hide
      */
     @ServiceState.FrequencyRange
     public int getFrequencyRange() {
@@ -139,7 +153,7 @@
 
     /**
      * @return the absolute radio frequency channel number for this physical channel,
-     * {@link Integer#MAX_VALUE} if unknown.
+     * {@link CHANNEL_NUMBER_UNKNOWN} if unknown.
      */
     public int getChannelNumber() {
         return mChannelNumber;
@@ -152,18 +166,20 @@
      * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
      * Reference: 3GPP TS 36.211 section 6.11.
      *
-     * In 5G RAN, this value is physical layer cell identity. The range is [0, 1008].
+     * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007].
      * Reference: 3GPP TS 38.211 section 7.4.2.1.
      *
-     * @return the physical cell identifier for this cell, {@link Integer#MAX_VALUE} if unknown.
+     * @return the physical cell identifier for this cell, {@link PHYSICAL_CELL_ID_UNKNOWN}
+     * if {@link android.telephony.CellInfo#UNAVAILABLE}.
      */
+    @IntRange(from = 0, to = 1007)
     public int getPhysicalCellId() {
         return mPhysicalCellId;
     }
 
     /**The radio technology for this physical channel. */
     @NetworkType
-    public int getRat() {
+    public int getNetworkType() {
         return mRat;
     }
 
@@ -181,7 +197,10 @@
         return mCellConnectionStatus;
     }
 
-    /** @return String representation of the connection status */
+    /**
+     * @return String representation of the connection status
+     * @hide
+     */
     private String getConnectionStatusString() {
         switch(mCellConnectionStatus) {
             case CONNECTION_PRIMARY_SERVING:
@@ -254,6 +273,12 @@
                 .toString();
     }
 
+    /** @hide */
+    public PhysicalChannelConfig(int status, int bandwidth) {
+        mCellConnectionStatus = status;
+        mCellBandwidthDownlinkKhz = bandwidth;
+    }
+
     private PhysicalChannelConfig(Parcel in) {
         mCellConnectionStatus = in.readInt();
         mCellBandwidthDownlinkKhz = in.readInt();
@@ -274,7 +299,10 @@
         mPhysicalCellId = builder.mPhysicalCellId;
     }
 
-    /** The builder of {@code PhysicalChannelConfig}. */
+    /**
+     * The builder of {@code PhysicalChannelConfig}.
+     * @hide
+     */
     public static final class Builder {
         private int mRat;
         private int mFrequencyRange;
@@ -284,60 +312,51 @@
         private int[] mContextIds;
         private int mPhysicalCellId;
 
-        /** @hide */
         public Builder() {
             mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
             mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
-            mChannelNumber = Integer.MAX_VALUE;
+            mChannelNumber = CHANNEL_NUMBER_UNKNOWN;
             mCellBandwidthDownlinkKhz = 0;
             mCellConnectionStatus = CONNECTION_UNKNOWN;
             mContextIds = new int[0];
-            mPhysicalCellId = Integer.MAX_VALUE;
+            mPhysicalCellId = PHYSICAL_CELL_ID_UNKNOWN;
         }
 
-        /** @hide */
         public PhysicalChannelConfig build() {
             return new PhysicalChannelConfig(this);
         }
 
-        /** @hide */
         public Builder setRat(int rat) {
             this.mRat = rat;
             return this;
         }
 
-        /** @hide */
         public Builder setFrequencyRange(int frequencyRange) {
             this.mFrequencyRange = frequencyRange;
             return this;
         }
 
-        /** @hide */
         public Builder setChannelNumber(int channelNumber) {
             this.mChannelNumber = channelNumber;
             return this;
         }
 
-        /** @hide */
         public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
             this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
             return this;
         }
 
-        /** @hide */
         public Builder setCellConnectionStatus(int connectionStatus) {
             this.mCellConnectionStatus = connectionStatus;
             return this;
         }
 
-        /** @hide */
         public Builder setContextIds(int[] contextIds) {
             if (contextIds != null) Arrays.sort(contextIds);
             this.mContextIds = contextIds;
             return this;
         }
 
-        /** @hide */
         public Builder setPhysicalCellId(int physicalCellId) {
             this.mPhysicalCellId = physicalCellId;
             return this;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7a77922..969016b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5555,8 +5555,49 @@
      * @param events The telephony state(s) of interest to the listener,
      *               as a bitwise-OR combination of {@link PhoneStateListener}
      *               LISTEN_ flags.
+     * @deprecated use {@link #listen(long, PhoneStateListener) instead due to the event number
+     *             limit increased to 64.
      */
+    @Deprecated
     public void listen(PhoneStateListener listener, int events) {
+        listen(events, listener);
+    }
+
+    /**
+     * Registers a listener object to receive notification of changes
+     * in specified telephony states.
+     * <p>
+     * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony
+     * state of interest in the events argument.
+     *
+     * At registration, and when a specified telephony state changes, the telephony manager invokes
+     * the appropriate callback method on the listener object and passes the current (updated)
+     * values.
+     * <p>
+     * To un-register a listener, pass the listener object and set the events argument to
+     * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+     *
+     * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+     * applies to the given subId. Otherwise, applies to
+     * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
+     * pass a separate listener object to each TelephonyManager object created with
+     * {@link #createForSubscriptionId}.
+     *
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
+     *
+     * This API should be used sparingly -- large numbers of listeners will cause system
+     * instability. If a process has registered too many listeners without unregistering them, it
+     * may encounter an {@link IllegalStateException} when trying to register more listeners.
+     *
+     * @param events The telephony state(s) of interest to the listener,
+     *               as a bitwise-OR combination of {@link PhoneStateListener}
+     *               LISTEN_ flags.
+     * @param listener The {@link PhoneStateListener} object to register
+     *                 (or unregister)
+     */
+    public void listen(long events, @NonNull PhoneStateListener listener) {
         if (mContext == null) return;
         boolean notifyNow = (getITelephony() != null);
         TelephonyRegistryManager telephonyRegistry =
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 952997e..943d783 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -18,7 +18,7 @@
     name: "FlickerTests",
     srcs: ["src/**/*.java", "src/**/*.kt"],
     manifest: "AndroidManifest.xml",
-    test_config: "AndroidTest.xml",
+    test_config: "AndroidTestPhysicalDevices.xml",
     platform_apis: true,
     certificate: "platform",
     test_suites: ["device-tests"],
@@ -33,3 +33,24 @@
         "launcher-aosp-tapl"
     ],
 }
+
+
+android_test {
+    name: "FlickerTestsVirtual",
+    srcs: ["src/**/*.java", "src/**/*.kt"],
+    manifest: "AndroidManifest.xml",
+    test_config: "AndroidTestVirtualDevices.xml",
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+    libs: ["android.test.runner"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "flickertestapplib",
+        "flickerlib",
+        "truth-prebuilt",
+        "app-helpers-core",
+        "launcher-helper-lib",
+        "launcher-aosp-tapl"
+    ],
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTestPhysicalDevices.xml
similarity index 95%
copy from tests/FlickerTests/AndroidTest.xml
copy to tests/FlickerTests/AndroidTestPhysicalDevices.xml
index 68c99a3..1650438 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTestPhysicalDevices.xml
@@ -27,6 +27,7 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.server.wm.flicker"/>
+        <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
         <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTestVirtualDevices.xml
similarity index 95%
rename from tests/FlickerTests/AndroidTest.xml
rename to tests/FlickerTests/AndroidTestVirtualDevices.xml
index 68c99a3..222212a 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTestVirtualDevices.xml
@@ -27,6 +27,7 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.server.wm.flicker"/>
+        <option name="exclude-annotation" value="androidx.test.filters.RequiresDevice" />
         <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
         <option name="shell-timeout" value="6600s" />
         <option name="test-timeout" value="6000s" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index c1a3ed69..69b1187 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -109,7 +109,7 @@
     }
 
     if (startingPos == endingPos) {
-        all("navBarLayerRotatesAndScales", enabled, bugId) {
+        all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
             this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 80d0394..404c789 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
 import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -39,7 +39,7 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToAppTest(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c0658fe..b64811b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
 import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -39,7 +39,7 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToHomeTest(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 67c46d3..0940c19 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.NonRotationTestBase
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -39,7 +39,7 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 open class CloseImeWindowToAppTest(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index dcf3085..c2e87db 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.NonRotationTestBase
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -40,7 +40,7 @@
  * Test IME window closing to home transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 open class CloseImeWindowToHomeTest(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 5874a07..11ccb69 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.NonRotationTestBase
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -39,7 +39,7 @@
  * Test IME window opening transitions.
  * To run this test: `atest FlickerTests:OpenImeWindowTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenImeWindowTest(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 62337e9..1759072 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.launch
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
@@ -38,7 +38,7 @@
  * Test cold launch app from launcher.
  * To run this test: `atest FlickerTests:OpenAppColdTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppColdTest(
@@ -80,7 +80,7 @@
                     noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
                     navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
                     statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
-                    navBarLayerIsAlwaysVisible()
+                    navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
                     statusBarLayerIsAlwaysVisible(enabled = false)
                     wallpaperLayerBecomesInvisible()
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 57d6127..afadb58 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.launch
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
@@ -39,7 +39,7 @@
  * Test warm launch app.
  * To run this test: `atest FlickerTests:OpenAppWarmTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppWarmTest(
@@ -88,7 +88,7 @@
                     noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
                     navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
                     statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
+                    navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
                     statusBarLayerIsAlwaysVisible(enabled = false)
                     wallpaperLayerBecomesInvisible()
                 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
index 4acd975..9cfc033 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
@@ -18,7 +18,7 @@
 
 import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.expandPipWindow
@@ -41,7 +41,7 @@
  * Test Pip launch.
  * To run this test: `atest FlickerTests:PipToAppTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
index 04c2f59..deccc90 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
@@ -18,7 +18,7 @@
 
 import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.closePipWindow
@@ -42,7 +42,7 @@
  * Test Pip launch.
  * To run this test: `atest FlickerTests:PipToAppTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
index b6074cd..f40869c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
@@ -18,7 +18,7 @@
 
 import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.closePipWindow
@@ -41,7 +41,7 @@
  * Test Pip launch.
  * To run this test: `atest FlickerTests:PipToHomeTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 5e75e4a..0ca1508 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.server.wm.flicker.rotation
 
+import androidx.test.filters.RequiresDevice
 import android.view.Surface
-import androidx.test.filters.LargeTest
 import com.android.server.wm.flicker.NonRotationTestBase.Companion.SCREENSHOT_LAYER
 import com.android.server.wm.flicker.RotationTestBase
 import com.android.server.wm.flicker.StandardAppHelper
@@ -42,7 +42,7 @@
  * Cycle through supported app rotations.
  * To run this test: `atest FlickerTest:ChangeAppRotationTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class ChangeAppRotationTest(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index de87b41..33a823d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -20,14 +20,13 @@
 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
 import com.android.server.wm.flicker.RotationTestBase
 import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.helpers.stopPackage
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
@@ -48,7 +47,7 @@
  * Cycle through supported app rotations using seamless rotations.
  * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 147659548)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index 279092d..c5e48d9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.splitscreen
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.NonRotationTestBase
 import com.android.server.wm.flicker.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
@@ -43,7 +43,7 @@
  * Test open app to split screen.
  * To run this test: `atest FlickerTests:OpenAppToSplitScreenTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppToSplitScreenTest(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
index a08b2bf..91211ca 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -21,7 +21,7 @@
 import android.view.Surface
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import androidx.test.uiautomator.By
 import com.android.server.wm.flicker.FlickerTestBase
 import com.android.server.wm.flicker.StandardAppHelper
@@ -52,7 +52,7 @@
  *
  * Currently it runs only in 0 degrees because of b/156100803
  */
-@LargeTest
+@RequiresDevice
 @RunWith(AndroidJUnit4::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 159096424)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index e2d7839..5c7dcd9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.splitscreen
 
 import android.view.Surface
-import androidx.test.filters.LargeTest
+import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.NonRotationTestBase
 import com.android.server.wm.flicker.StandardAppHelper
 import com.android.server.wm.flicker.dsl.flicker
@@ -43,7 +43,7 @@
  * Test open app to split screen.
  * To run this test: `atest FlickerTests:SplitScreenToLauncherTest`
  */
-@LargeTest
+@RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class SplitScreenToLauncherTest(
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 4f5a305..7dd003e 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -15,6 +15,7 @@
 android_test {
     name: "RollbackTest",
     manifest: "RollbackTest/AndroidManifest.xml",
+    platform_apis: true,
     srcs: ["RollbackTest/src/**/*.java"],
     static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
     test_suites: ["general-tests"],
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index de51c5c..0db2b2a 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -175,7 +175,7 @@
             assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
 
             UserManager um = (UserManager) context.getSystemService(context.USER_SERVICE);
-            List<Integer> userIds = um.getUsers(true)
+            List<Integer> userIds = um.getAliveUsers()
                     .stream().map(user -> user.id).collect(Collectors.toList());
             assertThat(InstallUtils.isOnlyInstalledForUser(TestApp.A,
                     context.getUserId(), userIds)).isTrue();
diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp
index ca0a091..92e3efa 100644
--- a/tests/SilkFX/Android.bp
+++ b/tests/SilkFX/Android.bp
@@ -19,4 +19,10 @@
     srcs: ["**/*.java", "**/*.kt"],
     platform_apis: true,
     certificate: "platform",
+        static_libs: [
+        "androidx.core_core",
+        "androidx.appcompat_appcompat",
+        "com.google.android.material_material",
+        "androidx-constraintlayout_constraintlayout",
+    ],
 }
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index ca9550a..050e9c3 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -39,5 +39,8 @@
         <activity android:name=".hdr.GlowActivity"
             android:label="Glow Examples"/>
 
+        <activity android:name=".materials.GlassActivity"
+            android:label="Glass Examples"/>
+
     </application>
 </manifest>
diff --git a/tests/SilkFX/res/drawable-hdpi/background1.jpeg b/tests/SilkFX/res/drawable-hdpi/background1.jpeg
new file mode 100644
index 0000000..dcdfa7b
--- /dev/null
+++ b/tests/SilkFX/res/drawable-hdpi/background1.jpeg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-hdpi/background2.jpeg b/tests/SilkFX/res/drawable-hdpi/background2.jpeg
new file mode 100644
index 0000000..dc7ce84e
--- /dev/null
+++ b/tests/SilkFX/res/drawable-hdpi/background2.jpeg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-hdpi/background3.jpeg b/tests/SilkFX/res/drawable-hdpi/background3.jpeg
new file mode 100644
index 0000000..12b3429
--- /dev/null
+++ b/tests/SilkFX/res/drawable-hdpi/background3.jpeg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-hdpi/noise.png b/tests/SilkFX/res/drawable-hdpi/noise.png
new file mode 100644
index 0000000..053995d
--- /dev/null
+++ b/tests/SilkFX/res/drawable-hdpi/noise.png
Binary files differ
diff --git a/tests/SilkFX/res/layout/activity_glass.xml b/tests/SilkFX/res/layout/activity_glass.xml
new file mode 100644
index 0000000..a7b76bd
--- /dev/null
+++ b/tests/SilkFX/res/layout/activity_glass.xml
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2012, 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <ImageView
+        android:id="@+id/background"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:scaleType="matrix"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:srcCompat="@drawable/background1" />
+
+    <com.android.test.silkfx.materials.GlassView
+        android:id="@+id/materialView"
+        android:layout_width="0dp"
+        android:layout_height="180dp"
+        android:layout_marginEnd="64dp"
+        android:layout_marginStart="64dp"
+        app:layout_constraintBottom_toTopOf="@+id/bottomPanel"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/bottomPanel"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorBackground"
+        android:paddingTop="24dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent">
+
+    <SeekBar
+        android:id="@+id/materialOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="16dp"
+        android:max="100"
+        android:progress="12"
+        app:layout_constraintBottom_toTopOf="@+id/scrimOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/zoom"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginStart="12dp"
+        android:min="-100"
+        android:max="100"
+        android:progress="-15"
+        app:layout_constraintBottom_toTopOf="@+id/blurRadiusTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/blurRadius"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginStart="12dp"
+        android:max="150"
+        android:progress="20"
+        app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/scrimOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="16dp"
+        android:max="100"
+        android:progress="50"
+        app:layout_constraintBottom_toTopOf="@+id/noiseOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/noiseOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="24dp"
+        android:max="100"
+        android:progress="5"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/scrimOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Scrim Opacity"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/scrimOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/materialOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Material Opacity"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/materialOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/zoomTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Zoom"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/zoom"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/blurRadiusTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Blur Radius"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/blurRadius"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/noiseOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:textColor="@android:color/white"
+        android:text="Noise Opacity"
+        app:layout_constraintBottom_toTopOf="@+id/noiseOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <ImageView
+        android:id="@+id/background1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="16dp"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toTopOf="@+id/lightMaterialSwitch"
+        app:layout_constraintStart_toStartOf="parent"
+        android:src="@drawable/background1" />
+
+    <ImageView
+        android:id="@+id/background2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background1"
+        android:src="@drawable/background2" />
+
+    <ImageView
+        android:id="@+id/background3"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:scaleType="centerCrop"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background2"
+        android:src="@drawable/background3" />
+
+    <Switch
+        android:id="@+id/lightMaterialSwitch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Light Material"
+        app:layout_constraintBottom_toTopOf="@+id/zoomTitle"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/blurRadiusValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/blurRadiusTitle"
+        app:layout_constraintStart_toEndOf="@+id/blurRadiusTitle" />
+
+    <TextView
+        android:id="@+id/zoomValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/zoomTitle"
+        app:layout_constraintStart_toEndOf="@+id/zoomTitle" />
+
+    <TextView
+        android:id="@+id/materialOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/materialOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/materialOpacityTitle" />
+
+    <TextView
+        android:id="@+id/noiseOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/noiseOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/noiseOpacityTitle" />
+
+
+    <TextView
+        android:id="@+id/scrimOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/scrimOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/scrimOpacityTitle" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
index 76e62a6..9ed8d2f 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -29,6 +29,7 @@
 import com.android.test.silkfx.app.EXTRA_LAYOUT
 import com.android.test.silkfx.app.EXTRA_TITLE
 import com.android.test.silkfx.hdr.GlowActivity
+import com.android.test.silkfx.materials.GlassActivity
 import kotlin.reflect.KClass
 
 class Demo(val name: String, val makeIntent: (Context) -> Intent) {
@@ -48,6 +49,9 @@
         DemoGroup("HDR", listOf(
                 Demo("Glow", GlowActivity::class),
                 Demo("Blingy Notifications", R.layout.bling_notifications)
+        )),
+        DemoGroup("Materials", listOf(
+                Demo("Glass", GlassActivity::class)
         ))
 )
 
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
new file mode 100644
index 0000000..72b342c
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 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.test.silkfx.materials
+
+import android.app.Activity
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.os.Bundle
+import android.util.TypedValue
+import android.view.View
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.Switch
+import android.widget.TextView
+import com.android.test.silkfx.R
+
+class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
+
+    lateinit var backgroundButton1: ImageView
+    lateinit var backgroundButton2: ImageView
+    lateinit var backgroundButton3: ImageView
+    lateinit var backgroundView: ImageView
+    lateinit var materialView: GlassView
+    lateinit var lightMaterialSwitch: Switch
+    lateinit var noiseOpacitySeekBar: SeekBar
+    lateinit var materialOpacitySeekBar: SeekBar
+    lateinit var scrimOpacitySeekBar: SeekBar
+    lateinit var zoomSeekBar: SeekBar
+    lateinit var blurRadiusSeekBar: SeekBar
+    lateinit var noiseOpacityValue: TextView
+    lateinit var materialOpacityValue: TextView
+    lateinit var scrimOpacityValue: TextView
+    lateinit var blurRadiusValue: TextView
+    lateinit var zoomValue: TextView
+
+    lateinit var background: Bitmap
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_glass)
+        backgroundButton1 = requireViewById(R.id.background1)
+        backgroundButton2 = requireViewById(R.id.background2)
+        backgroundButton3 = requireViewById(R.id.background3)
+        backgroundView = requireViewById(R.id.background)
+        lightMaterialSwitch = requireViewById(R.id.lightMaterialSwitch)
+        materialView = requireViewById(R.id.materialView)
+        materialOpacitySeekBar = requireViewById(R.id.materialOpacity)
+        blurRadiusSeekBar = requireViewById(R.id.blurRadius)
+        zoomSeekBar = requireViewById(R.id.zoom)
+        noiseOpacitySeekBar = requireViewById(R.id.noiseOpacity)
+        scrimOpacitySeekBar = requireViewById(R.id.scrimOpacity)
+        noiseOpacityValue = requireViewById(R.id.noiseOpacityValue)
+        materialOpacityValue = requireViewById(R.id.materialOpacityValue)
+        scrimOpacityValue = requireViewById(R.id.scrimOpacityValue)
+        blurRadiusValue = requireViewById(R.id.blurRadiusValue)
+        zoomValue = requireViewById(R.id.zoomValue)
+
+        background = BitmapFactory.decodeResource(resources, R.drawable.background1)
+        backgroundView.setImageBitmap(background)
+        materialView.backgroundBitmap = background
+
+        blurRadiusSeekBar.setOnSeekBarChangeListener(this)
+        materialOpacitySeekBar.setOnSeekBarChangeListener(this)
+        noiseOpacitySeekBar.setOnSeekBarChangeListener(this)
+        scrimOpacitySeekBar.setOnSeekBarChangeListener(this)
+
+        arrayOf(blurRadiusSeekBar, materialOpacitySeekBar, noiseOpacitySeekBar,
+                scrimOpacitySeekBar, zoomSeekBar).forEach {
+            it.setOnSeekBarChangeListener(this)
+            onProgressChanged(it, it.progress, fromUser = false)
+        }
+
+        lightMaterialSwitch.setOnCheckedChangeListener { _, isChecked ->
+            materialView.color = if (isChecked) Color.WHITE else Color.BLACK
+        }
+    }
+
+    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+        when (seekBar) {
+            blurRadiusSeekBar -> {
+                materialView.blurRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                progress.toFloat(), resources.displayMetrics)
+                blurRadiusValue.text = progress.toString()
+            }
+            materialOpacitySeekBar -> {
+                materialView.materialOpacity = progress / seekBar.max.toFloat()
+                materialOpacityValue.text = progress.toString()
+            }
+            noiseOpacitySeekBar -> {
+                materialView.noiseOpacity = progress / seekBar.max.toFloat()
+                noiseOpacityValue.text = progress.toString()
+            }
+            scrimOpacitySeekBar -> {
+                materialView.scrimOpacity = progress / seekBar.max.toFloat()
+                scrimOpacityValue.text = progress.toString()
+            }
+            zoomSeekBar -> {
+                materialView.zoom = progress / seekBar.max.toFloat()
+                zoomValue.text = progress.toString()
+            }
+            else -> throw IllegalArgumentException("Unknown seek bar")
+        }
+    }
+
+    override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+    override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+
+    fun onBackgroundClick(view: View) {
+        val resource = when (view) {
+            backgroundButton1 -> R.drawable.background1
+            backgroundButton2 -> R.drawable.background2
+            backgroundButton3 -> R.drawable.background3
+            else -> throw IllegalArgumentException("Invalid button")
+        }
+
+        background = BitmapFactory.decodeResource(resources, resource)
+        backgroundView.setImageBitmap(background)
+        materialView.backgroundBitmap = background
+    }
+}
\ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
new file mode 100644
index 0000000..6079768
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 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.test.silkfx.materials
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.BitmapShader
+import android.graphics.BlendMode
+import android.graphics.BlurShader
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Outline
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.Shader
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewOutlineProvider
+import com.android.test.silkfx.R
+
+class GlassView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
+
+    var noise = BitmapFactory.decodeResource(resources, R.drawable.noise)
+    var materialPaint = Paint()
+    var scrimPaint = Paint()
+    var noisePaint = Paint()
+    var blurPaint = Paint()
+
+    val src = Rect()
+    val dst = Rect()
+
+    var backgroundBitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+    set(value) {
+        field = value
+        invalidate()
+    }
+
+    var noiseOpacity = 0.0f
+    set(value) {
+        field = value
+        noisePaint.alpha = (value * 255).toInt()
+        invalidate()
+    }
+
+    var materialOpacity = 0.0f
+    set(value) {
+        field = value
+        materialPaint.alpha = (value * 255).toInt()
+        invalidate()
+    }
+
+    var scrimOpacity = 0.5f
+        set(value) {
+            field = value
+            scrimPaint.alpha = (value * 255).toInt()
+            invalidate()
+        }
+
+    var zoom = 0.0f
+        set(value) {
+            field = value
+            invalidate()
+        }
+
+    var color = Color.BLACK
+    set(value) {
+        field = value
+        var alpha = materialPaint.alpha
+        materialPaint.color = color
+        materialPaint.alpha = alpha
+
+        alpha = scrimPaint.alpha
+        scrimPaint.color = color
+        scrimPaint.alpha = alpha
+        invalidate()
+    }
+
+    var blurRadius = 150f
+    set(value) {
+        field = value
+        blurPaint.shader = BlurShader(value, value, null)
+        invalidate()
+    }
+
+    init {
+        materialPaint.blendMode = BlendMode.SOFT_LIGHT
+        noisePaint.blendMode = BlendMode.SOFT_LIGHT
+        noisePaint.shader = BitmapShader(noise, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
+        scrimPaint.alpha = (scrimOpacity * 255).toInt()
+        noisePaint.alpha = (noiseOpacity * 255).toInt()
+        materialPaint.alpha = (materialOpacity * 255).toInt()
+        blurPaint.shader = BlurShader(blurRadius, blurRadius, null)
+        outlineProvider = object : ViewOutlineProvider() {
+            override fun getOutline(view: View?, outline: Outline?) {
+                outline?.setRoundRect(Rect(0, 0, width, height), 100f)
+            }
+        }
+        clipToOutline = true
+    }
+
+    override fun onDraw(canvas: Canvas?) {
+        src.set(-width/2, -height/2, width/2, height/2)
+        src.scale(1.0f + zoom)
+        val centerX = left + width / 2
+        val centerY = top + height / 2
+        src.set(src.left + centerX, src.top + centerY, src.right + centerX, src.bottom + centerY)
+
+        dst.set(0, 0, width, height)
+        canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint)
+        canvas?.drawRect(dst, materialPaint)
+        canvas?.drawRect(dst, noisePaint)
+        canvas?.drawRect(dst, scrimPaint)
+    }
+}
\ No newline at end of file
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index f50d2e1..702f871 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -84,7 +84,8 @@
             Log.e(TAG, e);
         }
         deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
-                "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
+                "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
+                "/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex");
     }
 
     @Before
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a3673df..1f23bf3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -218,7 +218,6 @@
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -1199,7 +1198,7 @@
         MockitoAnnotations.initMocks(this);
         when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
 
-        when(mUserManager.getUsers(eq(true))).thenReturn(
+        when(mUserManager.getAliveUsers()).thenReturn(
                 Arrays.asList(new UserInfo[] {
                         new UserInfo(VPN_USER, "", 0),
                 }));
@@ -4028,7 +4027,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 140305589)
     public void testPacketKeepalives() throws Exception {
         InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
         InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 5a29c2c..de35f91 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -123,7 +123,7 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
-        when(mUserManager.getUsers(eq(true))).thenReturn(
+        when(mUserManager.getAliveUsers()).thenReturn(
                 Arrays.asList(new UserInfo[] {
                         new UserInfo(MOCK_USER1, "", 0),
                         new UserInfo(MOCK_USER2, "", 0),
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index e8c4ee9..c76b4cd 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1238,15 +1238,14 @@
          * @see UserManagerService#getUsers(boolean)
          */
         doAnswer(invocation -> {
-            final boolean excludeDying = (boolean) invocation.getArguments()[0];
             final ArrayList<UserInfo> result = new ArrayList<>(users.length);
             for (UserInfo ui : users) {
-                if (!excludeDying || (ui.isEnabled() && !ui.partial)) {
+                if (ui.isEnabled() && !ui.partial) {
                     result.add(ui);
                 }
             }
             return result;
-        }).when(mUserManager).getUsers(anyBoolean());
+        }).when(mUserManager).getAliveUsers();
 
         doAnswer(invocation -> {
             final int id = (int) invocation.getArguments()[0];
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 5ac9dfd..7c150f9 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -16,8 +16,8 @@
 
 #include <input/KeyCharacterMap.h>
 #include <input/KeyLayoutMap.h>
+#include <input/PropertyMap.h>
 #include <input/VirtualKeyMap.h>
-#include <utils/PropertyMap.h>
 
 #include <stdarg.h>
 #include <stdio.h>
@@ -96,21 +96,19 @@
         return false;
 
     case FILETYPE_KEYLAYOUT: {
-        sp<KeyLayoutMap> map;
-        status_t status = KeyLayoutMap::load(filename, &map);
-        if (status) {
-            error("Error %d parsing key layout file.\n\n", status);
+        base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(filename);
+        if (!ret) {
+            error("Error %s parsing key layout file.\n\n", ret.error().message().c_str());
             return false;
         }
         break;
     }
 
     case FILETYPE_KEYCHARACTERMAP: {
-        sp<KeyCharacterMap> map;
-        status_t status = KeyCharacterMap::load(filename,
-                KeyCharacterMap::FORMAT_ANY, &map);
-        if (status) {
-            error("Error %d parsing key character map file.\n\n", status);
+        base::Result<std::shared_ptr<KeyCharacterMap>> ret = KeyCharacterMap::load(filename,
+                KeyCharacterMap::FORMAT_ANY);
+        if (!ret) {
+            error("Error %s parsing key character map file.\n\n", ret.error().message().c_str());
             return false;
         }
         break;
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 941ff61..3040041 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -129,12 +129,8 @@
     },
     hostdex: true, // for hiddenapi check
 
-    // Allow access to the stubs from anywhere.
-    visibility: ["//visibility:public"],
-
     // Restrict access to implementation library.
     impl_library_visibility: [
-        "//visibility:override", // Ignore the visibility property.
         "//frameworks/opt/net/wifi/service:__subpackages__",
     ] + test_access_hidden_api_whitelist,
 
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index ee7320f..d0742c7 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -507,6 +507,7 @@
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setBssid(@NonNull android.net.MacAddress);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setCredentialSharedWithUser(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean);
+    method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedMacRandomizationEnabled(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsInitialAutojoinEnabled(boolean);
@@ -578,6 +579,7 @@
     method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
     method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
     method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
+    method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
     method public void onSessionConfigFailed();
     method public void onSessionConfigUpdated();
     method public void onSessionTerminated();
@@ -653,6 +655,7 @@
     method public void attach(@NonNull android.net.wifi.aware.AttachCallback, @NonNull android.net.wifi.aware.IdentityChangedListener, @Nullable android.os.Handler);
     method public android.net.wifi.aware.Characteristics getCharacteristics();
     method public boolean isAvailable();
+    method public boolean isDeviceAttached();
     field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
     field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index aa3a139..54ec1e1 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -81,6 +81,12 @@
     public String capabilities;
 
     /**
+     * The interface name on which the scan result was received.
+     * @hide
+     */
+    public String ifaceName;
+
+    /**
      * @hide
      * No security protocol.
      */
@@ -589,6 +595,27 @@
     public static final int BAND_6_GHZ_END_FREQ_MHZ = 7105;
 
     /**
+     * 60 GHz band first channel number
+     * @hide
+     */
+    public static final int BAND_60_GHZ_FIRST_CH_NUM = 1;
+    /**
+     * 60 GHz band last channel number
+     * @hide
+     */
+    public static final int BAND_60_GHZ_LAST_CH_NUM = 6;
+    /**
+     * 60 GHz band frequency of first channel in MHz
+     * @hide
+     */
+    public static final int BAND_60_GHZ_START_FREQ_MHZ = 58320;
+    /**
+     * 60 GHz band frequency of last channel in MHz
+     * @hide
+     */
+    public static final int BAND_60_GHZ_END_FREQ_MHZ = 70200;
+
+    /**
      * Utility function to check if a frequency within 2.4 GHz band
      * @param freqMhz frequency in MHz
      * @return true if within 2.4GHz, false otherwise
@@ -622,6 +649,17 @@
     }
 
     /**
+     * Utility function to check if a frequency within 60 GHz band
+     * @param freqMhz
+     * @return true if within 60GHz, false otherwise
+     *
+     * @hide
+     */
+    public static boolean is60GHz(int freqMhz) {
+        return freqMhz >= BAND_60_GHZ_START_FREQ_MHZ && freqMhz <= BAND_60_GHZ_END_FREQ_MHZ;
+    }
+
+    /**
      * Utility function to convert channel number/band to frequency in MHz
      * @param channel number to convert
      * @param band of channel to convert
@@ -701,6 +739,13 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean is60GHz() {
+        return ScanResult.is60GHz(frequency);
+    }
+
+    /**
      *  @hide
      * anqp lines from supplicant BSS response
      */
@@ -939,6 +984,7 @@
             flags = source.flags;
             radioChainInfos = source.radioChainInfos;
             this.mWifiStandard = source.mWifiStandard;
+            this.ifaceName = source.ifaceName;
         }
     }
 
@@ -977,6 +1023,7 @@
         sb.append(", 80211mcResponder: ");
         sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported");
         sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos));
+        sb.append(", interface name: ").append(ifaceName);
         return sb.toString();
     }
 
@@ -1056,6 +1103,7 @@
         } else {
             dest.writeInt(0);
         }
+        dest.writeString((ifaceName != null) ? ifaceName.toString() : "");
     }
 
     /** Implement the Parcelable interface */
@@ -1134,6 +1182,7 @@
                         sr.radioChainInfos[i].level = in.readInt();
                     }
                 }
+                sr.ifaceName = in.readString();
                 return sr;
             }
 
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index 99c4eac..cf54f26 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.wifi.SoftApConfiguration.BandType;
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -176,7 +176,7 @@
      */
     @NonNull
     public int[] getSupportedChannelList(@BandType int band) {
-        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+        if (!SdkLevelUtil.isAtLeastS()) {
             throw new UnsupportedOperationException();
         }
         switch (band) {
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 393fe8d..bc837b3 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -22,7 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.MacAddress;
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -551,7 +551,7 @@
     @SystemApi
     @MacRandomizationSetting
     public int getMacRandomizationSetting() {
-        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+        if (!SdkLevelUtil.isAtLeastS()) {
             throw new UnsupportedOperationException();
         }
         return mMacRandomizationSetting;
@@ -1046,7 +1046,7 @@
         @NonNull
         public Builder setMacRandomizationSetting(
                 @MacRandomizationSetting int macRandomizationSetting) {
-            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+            if (!SdkLevelUtil.isAtLeastS()) {
                 throw new UnsupportedOperationException();
             }
             mMacRandomizationSetting = macRandomizationSetting;
diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java
index 4791275..40981f7 100644
--- a/wifi/java/android/net/wifi/SoftApInfo.java
+++ b/wifi/java/android/net/wifi/SoftApInfo.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.MacAddress;
-import android.os.Build;
+import android.net.wifi.util.SdkLevelUtil;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -138,7 +138,7 @@
      */
     @Nullable
     public MacAddress getBssid() {
-        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
+        if (!SdkLevelUtil.isAtLeastS()) {
             throw new UnsupportedOperationException();
         }
         return mBssid;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 1588bf7..656444e 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -596,6 +596,12 @@
     public static final int AP_BAND_5GHZ = 1;
 
     /**
+     * 60GHz band
+     * @hide
+     */
+    public static final int AP_BAND_60GHZ = 2;
+
+    /**
      * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability,
      * operating country code and current radio conditions.
      * @hide
@@ -2509,7 +2515,18 @@
     @KeyMgmt.KeyMgmtScheme
     public int getAuthType() {
         if (allowedKeyManagement.cardinality() > 1) {
-            throw new IllegalStateException("More than one auth type set");
+            if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
+                if (allowedKeyManagement.cardinality() == 2
+                        && allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+                    return KeyMgmt.WPA_EAP;
+                }
+                if (allowedKeyManagement.cardinality() == 3
+                        && allowedKeyManagement.get(KeyMgmt.IEEE8021X)
+                        && allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
+                    return KeyMgmt.SUITE_B_192;
+                }
+            }
+            throw new IllegalStateException("Invalid auth type set: " + allowedKeyManagement);
         }
         if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
             return KeyMgmt.WPA_PSK;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 77fa673..90edc45 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -30,6 +30,9 @@
 import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECParameterSpec;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -1442,4 +1445,50 @@
         }
         return TextUtils.isEmpty(getCaPath());
     }
+
+    /**
+     * Check if a given certificate Get the Suite-B cipher from the certificate
+     *
+     * @param x509Certificate Certificate to process
+     * @return true if the certificate OID matches the Suite-B requirements for RSA or ECDSA
+     * certificates, or false otherwise.
+     * @hide
+     */
+    public static boolean isSuiteBCipherCert(@Nullable X509Certificate x509Certificate) {
+        if (x509Certificate == null) {
+            return false;
+        }
+        final String sigAlgOid = x509Certificate.getSigAlgOID();
+
+        // Wi-Fi alliance requires the use of both ECDSA secp384r1 and RSA 3072 certificates
+        // in WPA3-Enterprise 192-bit security networks, which are also known as Suite-B-192
+        // networks, even though NSA Suite-B-192 mandates ECDSA only. The use of the term
+        // Suite-B was already coined in the IEEE 802.11-2016 specification for
+        // AKM 00-0F-AC but the test plan for WPA3-Enterprise 192-bit for APs mandates
+        // support for both RSA and ECDSA, and for STAs it mandates ECDSA and optionally
+        // RSA. In order to be compatible with all WPA3-Enterprise 192-bit deployments,
+        // we are supporting both types here.
+        if (sigAlgOid.equals("1.2.840.113549.1.1.12")) {
+            // sha384WithRSAEncryption
+            if (x509Certificate.getPublicKey() instanceof RSAPublicKey) {
+                final RSAPublicKey rsaPublicKey = (RSAPublicKey) x509Certificate.getPublicKey();
+                if (rsaPublicKey.getModulus() != null
+                        && rsaPublicKey.getModulus().bitLength() >= 3072) {
+                    return true;
+                }
+            }
+        } else if (sigAlgOid.equals("1.2.840.10045.4.3.3")) {
+            // ecdsa-with-SHA384
+            if (x509Certificate.getPublicKey() instanceof ECPublicKey) {
+                final ECPublicKey ecPublicKey = (ECPublicKey) x509Certificate.getPublicKey();
+                final ECParameterSpec ecParameterSpec = ecPublicKey.getParams();
+
+                if (ecParameterSpec != null && ecParameterSpec.getOrder() != null
+                        && ecParameterSpec.getOrder().bitLength() >= 384) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index b0213b0..e12bb91 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -78,12 +78,12 @@
         private @Nullable String mWpa3SaePassphrase;
         /**
          * The enterprise configuration details specifying the EAP method,
-         * certificates and other settings associated with the WPA-EAP networks.
+         * certificates and other settings associated with the WPA/WPA2-Enterprise networks.
          */
         private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
         /**
          * The enterprise configuration details specifying the EAP method,
-         * certificates and other settings associated with the SuiteB networks.
+         * certificates and other settings associated with the WPA3-Enterprise networks.
          */
         private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
         /**
@@ -243,7 +243,11 @@
 
         /**
          * Set the associated enterprise configuration for this network. Needed for authenticating
-         * to WPA3-SuiteB networks. See {@link WifiEnterpriseConfig} for description.
+         * to WPA3-Enterprise networks (standard and 192-bit security). See
+         * {@link WifiEnterpriseConfig} for description. For 192-bit security networks, both the
+         * client and CA certificates must be provided, and must be of type of either
+         * sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384
+         * (OID 1.2.840.10045.4.3.3).
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
@@ -284,8 +288,25 @@
             } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
                 configuration.enterpriseConfig = mWpa2EnterpriseConfig;
-            } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
-                configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
+            } else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise
+                if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS
+                        && WifiEnterpriseConfig.isSuiteBCipherCert(
+                        mWpa3EnterpriseConfig.getClientCertificate())
+                        && WifiEnterpriseConfig.isSuiteBCipherCert(
+                        mWpa3EnterpriseConfig.getCaCertificate())) {
+                    // WPA3-Enterprise in 192-bit security mode (Suite-B)
+                    configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
+                } else {
+                    // WPA3-Enterprise
+                    configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
+                    configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+                    configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                    configuration.allowedPairwiseCiphers.set(
+                            WifiConfiguration.PairwiseCipher.GCMP_256);
+                    configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+                    configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+                    configuration.requirePmf = true;
+                }
                 configuration.enterpriseConfig = mWpa3EnterpriseConfig;
             } else if (mIsEnhancedOpen) { // OWE network
                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index a3c4ae7..aa69963 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -72,12 +72,12 @@
         private @Nullable String mWpa3SaePassphrase;
         /**
          * The enterprise configuration details specifying the EAP method,
-         * certificates and other settings associated with the WPA-EAP networks.
+         * certificates and other settings associated with the WPA/WPA2-Enterprise networks.
          */
         private @Nullable WifiEnterpriseConfig mWpa2EnterpriseConfig;
         /**
          * The enterprise configuration details specifying the EAP method,
-         * certificates and other settings associated with the SuiteB networks.
+         * certificates and other settings associated with the WPA3-Enterprise networks.
          */
         private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
         /**
@@ -149,6 +149,11 @@
          */
         private boolean mIsNetworkUntrusted;
 
+        /**
+         * Whether this network will use enhanced MAC randomization.
+         */
+        private boolean mIsEnhancedMacRandomizationEnabled;
+
         public Builder() {
             mSsid = null;
             mBssid =  null;
@@ -171,6 +176,7 @@
             mWapiEnterpriseConfig = null;
             mIsNetworkUntrusted = false;
             mPriorityGroup = 0;
+            mIsEnhancedMacRandomizationEnabled = true;
         }
 
         /**
@@ -282,7 +288,11 @@
 
         /**
          * Set the associated enterprise configuration for this network. Needed for authenticating
-         * to WPA3 enterprise networks. See {@link WifiEnterpriseConfig} for description.
+         * to WPA3-Enterprise networks (standard and 192-bit security). See
+         * {@link WifiEnterpriseConfig} for description. For 192-bit security networks, both the
+         * client and CA certificates must be provided, and must be of type of either
+         * sha384WithRSAEncryption (OID 1.2.840.113549.1.1.12) or ecdsa-with-SHA384
+         * (OID 1.2.840.10045.4.3.3).
          *
          * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
          * @return Instance of {@link Builder} to enable chaining of the builder method.
@@ -394,6 +404,29 @@
         }
 
         /**
+         * Specifies the MAC randomization method.
+         * <p>
+         * Suggested networks will never use the device (factory) MAC address to associate to the
+         * network - instead they use a locally generated random MAC address. This method controls
+         * the strategy for generating the random MAC address:
+         * <li> Persisted MAC randomization (false): generates the MAC address from a secret seed
+         * and information from the Wi-Fi configuration (SSID or Passpoint profile). That means that
+         * the same generated MAC address will be used for each subsequent association. </li>
+         * <li> Enhanced MAC randomization (true - the default): periodically generates a new MAC
+         * address new connections. Under this option, the randomized MAC address should change
+         * if the suggestion is removed and then added back. </li>
+         *
+         * @param enabled {@code true} to periodically change the randomized MAC address.
+         *                {@code false} to use the same randomized MAC for all connections to this
+         *                            network.
+         * @return Instance of {@link Builder} to enable chaining of the builder method.
+         */
+        public @NonNull Builder setIsEnhancedMacRandomizationEnabled(boolean enabled) {
+            mIsEnhancedMacRandomizationEnabled = enabled;
+            return this;
+        }
+
+        /**
          * Specifies whether the app needs to log in to a captive portal to obtain Internet access.
          * <p>
          * This will dictate if the directed broadcast
@@ -541,8 +574,25 @@
             } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network
                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
                 configuration.enterpriseConfig = mWpa2EnterpriseConfig;
-            } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network
-                configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
+            } else if (mWpa3EnterpriseConfig != null) { // WPA3-Enterprise
+                if (mWpa3EnterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS
+                        && WifiEnterpriseConfig.isSuiteBCipherCert(
+                        mWpa3EnterpriseConfig.getClientCertificate())
+                        && WifiEnterpriseConfig.isSuiteBCipherCert(
+                        mWpa3EnterpriseConfig.getCaCertificate())) {
+                    // WPA3-Enterprise in 192-bit security mode (Suite-B)
+                    configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
+                } else {
+                    // WPA3-Enterprise
+                    configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
+                    configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+                    configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+                    configuration.allowedPairwiseCiphers.set(
+                            WifiConfiguration.PairwiseCipher.GCMP_256);
+                    configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+                    configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
+                    configuration.requirePmf = true;
+                }
                 configuration.enterpriseConfig = mWpa3EnterpriseConfig;
             } else if (mIsEnhancedOpen) { // OWE network
                 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
@@ -577,6 +627,9 @@
             wifiConfiguration.meteredOverride = mMeteredOverride;
             wifiConfiguration.carrierId = mCarrierId;
             wifiConfiguration.trusted = !mIsNetworkUntrusted;
+            wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled
+                    ? WifiConfiguration.RANDOMIZATION_ENHANCED
+                    : WifiConfiguration.RANDOMIZATION_PERSISTENT;
             return wifiConfiguration;
         }
 
@@ -607,6 +660,9 @@
             wifiConfiguration.trusted = !mIsNetworkUntrusted;
             mPasspointConfiguration.setCarrierId(mCarrierId);
             mPasspointConfiguration.setMeteredOverride(wifiConfiguration.meteredOverride);
+            wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled
+                    ? WifiConfiguration.RANDOMIZATION_ENHANCED
+                    : WifiConfiguration.RANDOMIZATION_PERSISTENT;
             return wifiConfiguration;
         }
 
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index bfb0462..e3800ad 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -189,4 +189,16 @@
     public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
         /* empty */
     }
+
+    /**
+     * Called when the discovered peer is no longer visible. All further operations on this
+     * discovery session will fail. If the peer is visible again,
+     * {@link #onServiceDiscovered(PeerHandle, byte[], List)} or
+     * {@link #onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)} will be called.
+     *
+     * @param peerHandle An opaque handle to the peer matching our discovery operation.
+     */
+    public void onServiceLost(@NonNull PeerHandle peerHandle) {
+        /* empty */
+    }
 }
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
index 421a8af..e3e7c8e 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
@@ -35,4 +35,5 @@
     void onMessageSendSuccess(int messageId);
     void onMessageSendFail(int messageId, int reason);
     void onMessageReceived(int peerId, in byte[] message);
+    void onMatchExpired(int peerId);
 }
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 88f95ad..f5b1edc 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -36,6 +36,7 @@
     // Aware API
     boolean isUsageEnabled();
     Characteristics getCharacteristics();
+    boolean isDeviceAttached();
 
     // client API
     void connect(in IBinder binder, in String callingPackage, in String callingFeatureId,
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index c2ae17c..d6e46fd 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -179,6 +179,22 @@
     }
 
     /**
+     * Return the current status of the Aware service: whether ot not the device is already attached
+     * to an Aware cluster. To attach to an Aware cluster, please use
+     * {@link #attach(AttachCallback, Handler)} or
+     * {@link #attach(AttachCallback, IdentityChangedListener, Handler)}.
+     * @return A boolean indicating whether the device is attached to a cluster at this time (true)
+     *         or not (false).
+     */
+    public boolean isDeviceAttached() {
+        try {
+            return mService.isDeviceAttached();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify
      * limitations on configurations, e.g. the maximum service name length.
      *
@@ -587,6 +603,7 @@
         private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
         private static final int CALLBACK_MESSAGE_RECEIVED = 7;
         private static final int CALLBACK_MATCH_WITH_DISTANCE = 8;
+        private static final int CALLBACK_MATCH_EXPIRED = 9;
 
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
@@ -676,6 +693,9 @@
                             mOriginalCallback.onMessageReceived(new PeerHandle(msg.arg1),
                                     (byte[]) msg.obj);
                             break;
+                        case CALLBACK_MATCH_EXPIRED:
+                            mOriginalCallback
+                                    .onServiceLost(new PeerHandle(msg.arg1));
                     }
                 }
             };
@@ -746,6 +766,15 @@
             onMatchCommon(CALLBACK_MATCH_WITH_DISTANCE, peerId, serviceSpecificInfo, matchFilter,
                     distanceMm);
         }
+        @Override
+        public void onMatchExpired(int peerId) {
+            if (VDBG) {
+                Log.v(TAG, "onMatchExpired: peerId=" + peerId);
+            }
+            Message msg = mHandler.obtainMessage(CALLBACK_MATCH_EXPIRED);
+            msg.arg1 = peerId;
+            mHandler.sendMessage(msg);
+        }
 
         @Override
         public void onMessageSendSuccess(int messageId) {
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
index 4116234..3175e45 100644
--- a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -223,7 +223,11 @@
     /**
      * Callbacks for SoftAp interface registered using
      * {@link #registerApCallback(String, Executor, SoftApCallback)}.
+     *
+     * @deprecated The usage is replaced by vendor HAL
+     * {@code android.hardware.wifi.hostapd.V1_3.IHostapdCallback}.
      */
+    @Deprecated
     public interface SoftApCallback {
         /**
          * Invoked when there is a fatal failure and the SoftAp is shutdown.
@@ -1121,7 +1125,11 @@
      * @param callback Callback for AP events.
      * @return true on success, false on failure (e.g. when called on an interface which has not
      * been set up).
+     *
+     * @deprecated The usage is replaced by vendor HAL
+     * {@code android.hardware.wifi.hostapd.V1_3.IHostapdCallback}.
      */
+    @Deprecated
     public boolean registerApCallback(@NonNull String ifaceName,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull SoftApCallback callback) {
diff --git a/wifi/java/android/net/wifi/util/SdkLevelUtil.java b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
new file mode 100644
index 0000000..042634c7
--- /dev/null
+++ b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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.net.wifi.util;
+
+import android.os.Build;
+
+/**
+ * Utility to check the SDK version of the device that the code is running on.
+ *
+ * This can be used to disable new Wifi APIs added in Mainline updates on older SDK versions.
+ *
+ * @hide
+ */
+public class SdkLevelUtil {
+
+    /** This class is instantiable to allow easy mocking. */
+    public SdkLevelUtil() { }
+
+    /** See {@link #isAtLeastS()}. This version is non-static to allow easy mocking. */
+    public boolean isAtLeastSMockable() {
+        return isAtLeastS();
+    }
+
+    /** Returns true if the Android platform SDK is at least "S", false otherwise. */
+    public static boolean isAtLeastS() {
+        // TODO(b/167575586): after S SDK finalization, this method should just be
+        //  `return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;`
+
+        // at least S: return true
+        // this condition only evaluates to true after S SDK finalization when VERSION_CODES.S
+        // is set to something like "31", before SDK finalization the value is "10000"
+        // Note that Build.VERSION_CODES.S is inlined at compile time. If it's inlined to 10000,
+        // this condition never evaluates to true.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            return true;
+        }
+
+        // Assume for now that S = R + 1
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
+            return true;
+        }
+
+        // R: check CODENAME
+        // Before S SDK finalization, SDK_INT = R = 30 i.e. remains on the previous version
+        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
+            // CODENAME = "REL" on R release builds
+            // CODENAME = "S" on S development builds
+            return "S".equals(Build.VERSION.CODENAME);
+        }
+
+        // older than R: return false
+        return false;
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/FakeKeys.java b/wifi/tests/src/android/net/wifi/FakeKeys.java
index 641b891..8aa6add 100644
--- a/wifi/tests/src/android/net/wifi/FakeKeys.java
+++ b/wifi/tests/src/android/net/wifi/FakeKeys.java
@@ -212,7 +212,57 @@
             (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
             (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
     };
-    public static final PrivateKey RSA_KEY1 = loadPrivateRSAKey(FAKE_RSA_KEY_1);
+    public static final PrivateKey RSA_KEY1 = loadPrivateKey("RSA", FAKE_RSA_KEY_1);
+
+    private static final String CA_SUITE_B_RSA3072_CERT_STRING =
+            "-----BEGIN CERTIFICATE-----\n"
+                    + "MIIEnTCCAwWgAwIBAgIUD87Y8fFLzLr1HQ/64aEnjNq2R/4wDQYJKoZIhvcNAQEM\n"
+                    + "BQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANNVFYxEDAO\n"
+                    + "BgNVBAoMB0FuZHJvaWQxDjAMBgNVBAsMBVdpLUZpMRIwEAYDVQQDDAl1bml0ZXN0\n"
+                    + "Q0EwHhcNMjAwNzIxMDIxNzU0WhcNMzAwNTMwMDIxNzU0WjBeMQswCQYDVQQGEwJV\n"
+                    + "UzELMAkGA1UECAwCQ0ExDDAKBgNVBAcMA01UVjEQMA4GA1UECgwHQW5kcm9pZDEO\n"
+                    + "MAwGA1UECwwFV2ktRmkxEjAQBgNVBAMMCXVuaXRlc3RDQTCCAaIwDQYJKoZIhvcN\n"
+                    + "AQEBBQADggGPADCCAYoCggGBAMtrsT0otlxh0QS079KpRRbU1PQjCihSoltXnrxF\n"
+                    + "sTWZs2weVEeYVyYU5LaauCDDgISCMtjtfbfylMBeYjpWB5hYzYQOiTzo0anWhMyb\n"
+                    + "Ngb7gpMVZuIl6lwMYRyVRKwHWnTo2EUg1ZzW5rGe5fs/KHj6//hoNFm+3Oju0TQd\n"
+                    + "nraQULpoERPF5B7p85Cssk8uNbviBfZXvtCuJ4N6w7PNceOY/9bbwc1mC+pPZmzV\n"
+                    + "SOAg0vvbIQRzChm63C3jBC3xmxSOOZVrKN4zKDG2s8P0oCNGt0NlgRMrgbPRekzg\n"
+                    + "4avkbA0vTuc2AyriTEYkdea/Mt4EpRg9XuOb43U/GJ/d/vQv2/9fsxhXmsZrn8kr\n"
+                    + "Qo5MMHJFUd96GgHmvYSU3Mf/5r8gF626lvqHioGuTAuHUSnr02ri1WUxZ15LDRgY\n"
+                    + "quMjDCFZfucjJPDAdtiHcFSej/4SLJlN39z8oKKNPn3aL9Gv49oAKs9S8tfDVzMk\n"
+                    + "fDLROQFHFuW715GnnMgEAoOpRwIDAQABo1MwUTAdBgNVHQ4EFgQUeVuGmSVN4ARs\n"
+                    + "mesUMWSJ2qWLbxUwHwYDVR0jBBgwFoAUeVuGmSVN4ARsmesUMWSJ2qWLbxUwDwYD\n"
+                    + "VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQwFAAOCAYEAit1Lo/hegZpPuT9dlWZJ\n"
+                    + "bC8JvAf95O8lnn6LFb69pgYOHCLgCIlvYXu9rdBUJgZo+V1MzJJljiO6RxWRfKbQ\n"
+                    + "8WBYkoqR1EqriR3Kn8q/SjIZCdFSaznTyU1wQMveBQ6RJWXSUhYVfE9RjyFTp7B4\n"
+                    + "UyH2uCluR/0T06HQNGfH5XpIYQqCk1Zgng5lmEmheLDPoJpa92lKeQFJMC6eYz9g\n"
+                    + "lF1GHxPxkPfbMJ6ZDp5X6Yopu6Q6uEXhVKM/iQVcgzRkx9rid+xTYl+nOKyK/XfC\n"
+                    + "z8P0/TFIoPTW02DLge5wKagdoCpy1B7HdrAXyUjoH4B8MsUkq3kYPFSjPzScuTtV\n"
+                    + "kUuDw5ipCNeXCRnhbYqRDk6PX5GUu2cmN9jtaH3tbgm3fKNOsd/BO1fLIl7qjXlR\n"
+                    + "27HHbC0JXjNvlm2DLp23v4NTxS7WZGYsxyUj5DZrxBxqCsTXu/01w1BrQKWKh9FM\n"
+                    + "aVrlA8omfVODK2CSuw+KhEMHepRv/AUgsLl4L4+RMoa+\n"
+                    + "-----END CERTIFICATE-----\n";
+    public static final X509Certificate CA_SUITE_B_RSA3072_CERT =
+            loadCertificate(CA_SUITE_B_RSA3072_CERT_STRING);
+
+    private static final String CA_SUITE_B_ECDSA_CERT_STRING =
+            "-----BEGIN CERTIFICATE-----\n"
+                    + "MIICTzCCAdSgAwIBAgIUdnLttwNPnQzFufplGOr9bTrGCqMwCgYIKoZIzj0EAwMw\n"
+                    + "XjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANNVFYxEDAOBgNV\n"
+                    + "BAoMB0FuZHJvaWQxDjAMBgNVBAsMBVdpLUZpMRIwEAYDVQQDDAl1bml0ZXN0Q0Ew\n"
+                    + "HhcNMjAwNzIxMDIyNDA1WhcNMzAwNTMwMDIyNDA1WjBeMQswCQYDVQQGEwJVUzEL\n"
+                    + "MAkGA1UECAwCQ0ExDDAKBgNVBAcMA01UVjEQMA4GA1UECgwHQW5kcm9pZDEOMAwG\n"
+                    + "A1UECwwFV2ktRmkxEjAQBgNVBAMMCXVuaXRlc3RDQTB2MBAGByqGSM49AgEGBSuB\n"
+                    + "BAAiA2IABFmntXwk9icqhDQFUP1xy04WyEpaGW4q6Q+8pujlSl/X3iotPZ++GZfp\n"
+                    + "Mfv3YDHDBl6sELPQ2BEjyPXmpsKjOUdiUe69e88oGEdeqT2xXiQ6uzpTfJD4170i\n"
+                    + "O/TwLrQGKKNTMFEwHQYDVR0OBBYEFCjptsX3g4g5W0L4oEP6N3gfyiZXMB8GA1Ud\n"
+                    + "IwQYMBaAFCjptsX3g4g5W0L4oEP6N3gfyiZXMA8GA1UdEwEB/wQFMAMBAf8wCgYI\n"
+                    + "KoZIzj0EAwMDaQAwZgIxAK61brUYRbLmQKiaEboZgrHtnPAcGo7Yzx3MwHecx3Dm\n"
+                    + "5soIeLVYc8bPYN1pbhXW1gIxALdEe2sh03nBHyQH4adYoZungoCwt8mp/7sJFxou\n"
+                    + "9UnRegyBgGzf74ROWdpZHzh+Pg==\n"
+                    + "-----END CERTIFICATE-----\n";
+    public static final X509Certificate CA_SUITE_B_ECDSA_CERT =
+            loadCertificate(CA_SUITE_B_ECDSA_CERT_STRING);
 
     private static final String CLIENT_SUITE_B_RSA3072_CERT_STRING =
             "-----BEGIN CERTIFICATE-----\n"
@@ -243,6 +293,363 @@
     public static final X509Certificate CLIENT_SUITE_B_RSA3072_CERT =
             loadCertificate(CLIENT_SUITE_B_RSA3072_CERT_STRING);
 
+    private static final byte[] CLIENT_SUITE_B_RSA3072_KEY_DATA = new byte[]{
+            (byte) 0x30, (byte) 0x82, (byte) 0x06, (byte) 0xfe, (byte) 0x02, (byte) 0x01,
+            (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+            (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+            (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
+            (byte) 0x06, (byte) 0xe8, (byte) 0x30, (byte) 0x82, (byte) 0x06, (byte) 0xe4,
+            (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x82, (byte) 0x01,
+            (byte) 0x81, (byte) 0x00, (byte) 0xc1, (byte) 0x22, (byte) 0xb7, (byte) 0x0b,
+            (byte) 0x92, (byte) 0xb9, (byte) 0xb9, (byte) 0xdb, (byte) 0x42, (byte) 0x29,
+            (byte) 0x39, (byte) 0xc4, (byte) 0xd7, (byte) 0x87, (byte) 0xbc, (byte) 0xcf,
+            (byte) 0x67, (byte) 0x19, (byte) 0xbf, (byte) 0x09, (byte) 0x81, (byte) 0xe1,
+            (byte) 0x77, (byte) 0xbe, (byte) 0x6b, (byte) 0xcf, (byte) 0xbb, (byte) 0x40,
+            (byte) 0xbb, (byte) 0x9d, (byte) 0x1e, (byte) 0x8a, (byte) 0x1c, (byte) 0xfe,
+            (byte) 0x54, (byte) 0x33, (byte) 0x0a, (byte) 0x58, (byte) 0x0a, (byte) 0xe0,
+            (byte) 0xc6, (byte) 0xd5, (byte) 0x50, (byte) 0x2d, (byte) 0x03, (byte) 0xdc,
+            (byte) 0x51, (byte) 0x3e, (byte) 0x53, (byte) 0x7d, (byte) 0x82, (byte) 0xef,
+            (byte) 0xc4, (byte) 0xb1, (byte) 0x2a, (byte) 0x84, (byte) 0xda, (byte) 0x45,
+            (byte) 0x6b, (byte) 0x6f, (byte) 0x3e, (byte) 0x63, (byte) 0x66, (byte) 0xf9,
+            (byte) 0x46, (byte) 0x85, (byte) 0x4f, (byte) 0xc2, (byte) 0xa4, (byte) 0xc3,
+            (byte) 0x25, (byte) 0x27, (byte) 0xa3, (byte) 0xf7, (byte) 0x6f, (byte) 0xfb,
+            (byte) 0x65, (byte) 0xc3, (byte) 0xa5, (byte) 0xdf, (byte) 0xf3, (byte) 0x01,
+            (byte) 0x14, (byte) 0x3e, (byte) 0xdc, (byte) 0x5c, (byte) 0x00, (byte) 0x7d,
+            (byte) 0x6a, (byte) 0x29, (byte) 0x02, (byte) 0x11, (byte) 0x32, (byte) 0x09,
+            (byte) 0x54, (byte) 0xb1, (byte) 0xc2, (byte) 0xc0, (byte) 0x9a, (byte) 0xfa,
+            (byte) 0xc9, (byte) 0x50, (byte) 0xe2, (byte) 0x3b, (byte) 0x91, (byte) 0x20,
+            (byte) 0xc2, (byte) 0x2e, (byte) 0x50, (byte) 0x2d, (byte) 0x4c, (byte) 0x9b,
+            (byte) 0x43, (byte) 0x5a, (byte) 0xa6, (byte) 0xd6, (byte) 0x72, (byte) 0x33,
+            (byte) 0x74, (byte) 0xe3, (byte) 0xfc, (byte) 0x80, (byte) 0x90, (byte) 0x11,
+            (byte) 0xfa, (byte) 0x64, (byte) 0xa3, (byte) 0xda, (byte) 0x95, (byte) 0x21,
+            (byte) 0xb8, (byte) 0x8a, (byte) 0xe9, (byte) 0xea, (byte) 0x09, (byte) 0x31,
+            (byte) 0x39, (byte) 0x18, (byte) 0xf0, (byte) 0x45, (byte) 0x9f, (byte) 0x02,
+            (byte) 0x7e, (byte) 0xd1, (byte) 0x4c, (byte) 0x57, (byte) 0x5f, (byte) 0x47,
+            (byte) 0x53, (byte) 0x8b, (byte) 0xb8, (byte) 0xed, (byte) 0x26, (byte) 0x54,
+            (byte) 0xe8, (byte) 0xe0, (byte) 0x2d, (byte) 0x6f, (byte) 0x7f, (byte) 0xfa,
+            (byte) 0xea, (byte) 0x58, (byte) 0xbf, (byte) 0xa8, (byte) 0x59, (byte) 0xd7,
+            (byte) 0xd9, (byte) 0xc0, (byte) 0x30, (byte) 0x0c, (byte) 0x70, (byte) 0xe1,
+            (byte) 0x04, (byte) 0xc9, (byte) 0xc7, (byte) 0xb9, (byte) 0x4b, (byte) 0xc0,
+            (byte) 0x02, (byte) 0xd7, (byte) 0xec, (byte) 0x1f, (byte) 0xad, (byte) 0x0d,
+            (byte) 0x83, (byte) 0x44, (byte) 0x64, (byte) 0x70, (byte) 0xea, (byte) 0x60,
+            (byte) 0xbd, (byte) 0xb3, (byte) 0xca, (byte) 0xf4, (byte) 0x16, (byte) 0x02,
+            (byte) 0x3d, (byte) 0x87, (byte) 0x0a, (byte) 0x57, (byte) 0xab, (byte) 0x7b,
+            (byte) 0xc4, (byte) 0x18, (byte) 0x20, (byte) 0xbc, (byte) 0x64, (byte) 0xbe,
+            (byte) 0x4b, (byte) 0x60, (byte) 0x06, (byte) 0x0d, (byte) 0x9c, (byte) 0xac,
+            (byte) 0x42, (byte) 0x49, (byte) 0x7b, (byte) 0x85, (byte) 0xdb, (byte) 0x0c,
+            (byte) 0x7e, (byte) 0xcb, (byte) 0x03, (byte) 0x7a, (byte) 0xeb, (byte) 0x5e,
+            (byte) 0x6b, (byte) 0x22, (byte) 0xa9, (byte) 0xfd, (byte) 0x59, (byte) 0x6d,
+            (byte) 0xf1, (byte) 0x45, (byte) 0x13, (byte) 0x32, (byte) 0xbd, (byte) 0x34,
+            (byte) 0x5a, (byte) 0xa8, (byte) 0xbc, (byte) 0xbf, (byte) 0xaa, (byte) 0x1a,
+            (byte) 0x1f, (byte) 0xb3, (byte) 0x20, (byte) 0xff, (byte) 0xb9, (byte) 0xf3,
+            (byte) 0xc4, (byte) 0xa1, (byte) 0x24, (byte) 0x53, (byte) 0xbd, (byte) 0x1f,
+            (byte) 0xf4, (byte) 0x43, (byte) 0x9c, (byte) 0x3a, (byte) 0x62, (byte) 0x4e,
+            (byte) 0x70, (byte) 0x05, (byte) 0x4d, (byte) 0x65, (byte) 0xd0, (byte) 0x75,
+            (byte) 0x3c, (byte) 0x20, (byte) 0xb3, (byte) 0x34, (byte) 0x92, (byte) 0xd1,
+            (byte) 0x5c, (byte) 0x36, (byte) 0x3c, (byte) 0x1f, (byte) 0x89, (byte) 0xa8,
+            (byte) 0x40, (byte) 0x01, (byte) 0x01, (byte) 0xaf, (byte) 0x43, (byte) 0x78,
+            (byte) 0xcb, (byte) 0xd7, (byte) 0x4f, (byte) 0x53, (byte) 0xb2, (byte) 0xf8,
+            (byte) 0xd6, (byte) 0x37, (byte) 0x22, (byte) 0xd3, (byte) 0xc7, (byte) 0xcb,
+            (byte) 0x2e, (byte) 0xb7, (byte) 0x9d, (byte) 0x06, (byte) 0x55, (byte) 0x23,
+            (byte) 0x6a, (byte) 0xd7, (byte) 0x00, (byte) 0xdc, (byte) 0x38, (byte) 0x36,
+            (byte) 0x1c, (byte) 0x12, (byte) 0xd1, (byte) 0x9e, (byte) 0x83, (byte) 0x17,
+            (byte) 0xe4, (byte) 0x2c, (byte) 0x4c, (byte) 0xda, (byte) 0xe3, (byte) 0xf8,
+            (byte) 0x65, (byte) 0x3b, (byte) 0x7b, (byte) 0x84, (byte) 0x86, (byte) 0xfc,
+            (byte) 0x41, (byte) 0x91, (byte) 0xf1, (byte) 0x2b, (byte) 0xe5, (byte) 0x76,
+            (byte) 0x36, (byte) 0x1f, (byte) 0x41, (byte) 0x35, (byte) 0x85, (byte) 0x2e,
+            (byte) 0x0d, (byte) 0x65, (byte) 0xfd, (byte) 0x44, (byte) 0xf5, (byte) 0x84,
+            (byte) 0xe3, (byte) 0xa4, (byte) 0x41, (byte) 0x9c, (byte) 0x1d, (byte) 0xb1,
+            (byte) 0xa5, (byte) 0xb5, (byte) 0xce, (byte) 0x02, (byte) 0xb2, (byte) 0x7a,
+            (byte) 0xe8, (byte) 0x85, (byte) 0x07, (byte) 0x62, (byte) 0x9d, (byte) 0x32,
+            (byte) 0x66, (byte) 0xc0, (byte) 0x4a, (byte) 0xaf, (byte) 0x94, (byte) 0xc7,
+            (byte) 0x52, (byte) 0xf5, (byte) 0x28, (byte) 0x80, (byte) 0xa8, (byte) 0xd0,
+            (byte) 0x88, (byte) 0x25, (byte) 0xc1, (byte) 0x67, (byte) 0x01, (byte) 0xff,
+            (byte) 0xc9, (byte) 0xe7, (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00,
+            (byte) 0x01, (byte) 0x02, (byte) 0x82, (byte) 0x01, (byte) 0x80, (byte) 0x04,
+            (byte) 0xb1, (byte) 0xcc, (byte) 0x53, (byte) 0x3a, (byte) 0xb0, (byte) 0xcb,
+            (byte) 0x04, (byte) 0xba, (byte) 0x59, (byte) 0xf8, (byte) 0x2e, (byte) 0x81,
+            (byte) 0xb2, (byte) 0xa9, (byte) 0xf3, (byte) 0x3c, (byte) 0xa5, (byte) 0x52,
+            (byte) 0x90, (byte) 0x6f, (byte) 0x98, (byte) 0xc4, (byte) 0x69, (byte) 0x5b,
+            (byte) 0x83, (byte) 0x84, (byte) 0x20, (byte) 0xb1, (byte) 0xae, (byte) 0xc3,
+            (byte) 0x04, (byte) 0x46, (byte) 0x6a, (byte) 0x24, (byte) 0x2f, (byte) 0xcd,
+            (byte) 0x6b, (byte) 0x90, (byte) 0x70, (byte) 0x20, (byte) 0x45, (byte) 0x25,
+            (byte) 0x1a, (byte) 0xc3, (byte) 0x02, (byte) 0x42, (byte) 0xf3, (byte) 0x49,
+            (byte) 0xe2, (byte) 0x3e, (byte) 0x21, (byte) 0x87, (byte) 0xdd, (byte) 0x6a,
+            (byte) 0x94, (byte) 0x2a, (byte) 0x1e, (byte) 0x0f, (byte) 0xdb, (byte) 0x77,
+            (byte) 0x5f, (byte) 0xc1, (byte) 0x2c, (byte) 0x03, (byte) 0xfb, (byte) 0xcf,
+            (byte) 0x91, (byte) 0x82, (byte) 0xa1, (byte) 0xbf, (byte) 0xb0, (byte) 0x73,
+            (byte) 0xfa, (byte) 0xda, (byte) 0xbc, (byte) 0xf8, (byte) 0x9f, (byte) 0x45,
+            (byte) 0xd3, (byte) 0xe8, (byte) 0xbb, (byte) 0x38, (byte) 0xfb, (byte) 0xc2,
+            (byte) 0x2d, (byte) 0x76, (byte) 0x51, (byte) 0x96, (byte) 0x18, (byte) 0x03,
+            (byte) 0x15, (byte) 0xd9, (byte) 0xea, (byte) 0x82, (byte) 0x25, (byte) 0x83,
+            (byte) 0xff, (byte) 0x5c, (byte) 0x85, (byte) 0x06, (byte) 0x09, (byte) 0xb2,
+            (byte) 0x46, (byte) 0x12, (byte) 0x64, (byte) 0x02, (byte) 0x74, (byte) 0x4f,
+            (byte) 0xbc, (byte) 0x9a, (byte) 0x25, (byte) 0x18, (byte) 0x01, (byte) 0x07,
+            (byte) 0x17, (byte) 0x25, (byte) 0x55, (byte) 0x7c, (byte) 0xdc, (byte) 0xe1,
+            (byte) 0xd1, (byte) 0x5a, (byte) 0x2f, (byte) 0x25, (byte) 0xaf, (byte) 0xf6,
+            (byte) 0x8f, (byte) 0xa4, (byte) 0x9a, (byte) 0x5a, (byte) 0x3a, (byte) 0xfe,
+            (byte) 0x2e, (byte) 0x93, (byte) 0x24, (byte) 0xa0, (byte) 0x27, (byte) 0xac,
+            (byte) 0x07, (byte) 0x75, (byte) 0x33, (byte) 0x01, (byte) 0x54, (byte) 0x23,
+            (byte) 0x0f, (byte) 0xe8, (byte) 0x9f, (byte) 0xfa, (byte) 0x36, (byte) 0xe6,
+            (byte) 0x3a, (byte) 0xd5, (byte) 0x78, (byte) 0xb0, (byte) 0xe4, (byte) 0x6a,
+            (byte) 0x16, (byte) 0x50, (byte) 0xbd, (byte) 0x0f, (byte) 0x9f, (byte) 0x32,
+            (byte) 0xa1, (byte) 0x6b, (byte) 0xf5, (byte) 0xa4, (byte) 0x34, (byte) 0x58,
+            (byte) 0xb6, (byte) 0xa4, (byte) 0xb3, (byte) 0xc3, (byte) 0x83, (byte) 0x08,
+            (byte) 0x18, (byte) 0xc7, (byte) 0xef, (byte) 0x95, (byte) 0xe2, (byte) 0x1b,
+            (byte) 0xba, (byte) 0x35, (byte) 0x61, (byte) 0xa3, (byte) 0xb4, (byte) 0x30,
+            (byte) 0xe0, (byte) 0xd1, (byte) 0xc1, (byte) 0xa2, (byte) 0x3a, (byte) 0xc6,
+            (byte) 0xb4, (byte) 0xd2, (byte) 0x80, (byte) 0x5a, (byte) 0xaf, (byte) 0xa4,
+            (byte) 0x54, (byte) 0x3c, (byte) 0x66, (byte) 0x5a, (byte) 0x1c, (byte) 0x4d,
+            (byte) 0xe1, (byte) 0xd9, (byte) 0x98, (byte) 0x44, (byte) 0x01, (byte) 0x1b,
+            (byte) 0x8c, (byte) 0xe9, (byte) 0x80, (byte) 0x54, (byte) 0x83, (byte) 0x3d,
+            (byte) 0x96, (byte) 0x25, (byte) 0x41, (byte) 0x1c, (byte) 0xad, (byte) 0xae,
+            (byte) 0x3b, (byte) 0x7a, (byte) 0xd7, (byte) 0x9d, (byte) 0x10, (byte) 0x7c,
+            (byte) 0xd1, (byte) 0xa7, (byte) 0x96, (byte) 0x39, (byte) 0xa5, (byte) 0x2f,
+            (byte) 0xbe, (byte) 0xc3, (byte) 0x2c, (byte) 0x64, (byte) 0x01, (byte) 0xfe,
+            (byte) 0xa2, (byte) 0xd1, (byte) 0x6a, (byte) 0xcf, (byte) 0x4c, (byte) 0x76,
+            (byte) 0x3b, (byte) 0xc8, (byte) 0x35, (byte) 0x21, (byte) 0xda, (byte) 0x98,
+            (byte) 0xcf, (byte) 0xf9, (byte) 0x29, (byte) 0xff, (byte) 0x30, (byte) 0x59,
+            (byte) 0x36, (byte) 0x53, (byte) 0x0b, (byte) 0xbb, (byte) 0xfa, (byte) 0xba,
+            (byte) 0xc4, (byte) 0x03, (byte) 0x23, (byte) 0xe0, (byte) 0xd3, (byte) 0x33,
+            (byte) 0xff, (byte) 0x32, (byte) 0xdb, (byte) 0x30, (byte) 0x64, (byte) 0xc7,
+            (byte) 0x56, (byte) 0xca, (byte) 0x55, (byte) 0x14, (byte) 0xee, (byte) 0x58,
+            (byte) 0xfe, (byte) 0x96, (byte) 0x7e, (byte) 0x1c, (byte) 0x34, (byte) 0x16,
+            (byte) 0xeb, (byte) 0x76, (byte) 0x26, (byte) 0x48, (byte) 0xe2, (byte) 0xe5,
+            (byte) 0x5c, (byte) 0xd5, (byte) 0x83, (byte) 0x37, (byte) 0xd9, (byte) 0x09,
+            (byte) 0x71, (byte) 0xbc, (byte) 0x54, (byte) 0x25, (byte) 0xca, (byte) 0x2e,
+            (byte) 0xdb, (byte) 0x36, (byte) 0x39, (byte) 0xcc, (byte) 0x3a, (byte) 0x81,
+            (byte) 0x95, (byte) 0x9e, (byte) 0xf4, (byte) 0x01, (byte) 0xa7, (byte) 0xc0,
+            (byte) 0x20, (byte) 0xce, (byte) 0x70, (byte) 0x55, (byte) 0x2c, (byte) 0xe0,
+            (byte) 0x93, (byte) 0x72, (byte) 0xa6, (byte) 0x25, (byte) 0xda, (byte) 0x64,
+            (byte) 0x19, (byte) 0x18, (byte) 0xd2, (byte) 0x31, (byte) 0xe2, (byte) 0x7c,
+            (byte) 0xf2, (byte) 0x30, (byte) 0x9e, (byte) 0x8d, (byte) 0xc6, (byte) 0x14,
+            (byte) 0x8a, (byte) 0x38, (byte) 0xf0, (byte) 0x94, (byte) 0xeb, (byte) 0xf4,
+            (byte) 0x64, (byte) 0x92, (byte) 0x3d, (byte) 0x67, (byte) 0xa6, (byte) 0x2c,
+            (byte) 0x52, (byte) 0xfc, (byte) 0x60, (byte) 0xca, (byte) 0x2a, (byte) 0xcf,
+            (byte) 0x24, (byte) 0xd5, (byte) 0x42, (byte) 0x5f, (byte) 0xc7, (byte) 0x9f,
+            (byte) 0xf3, (byte) 0xb4, (byte) 0xdf, (byte) 0x76, (byte) 0x6e, (byte) 0x53,
+            (byte) 0xa1, (byte) 0x7b, (byte) 0xae, (byte) 0xa5, (byte) 0x84, (byte) 0x1f,
+            (byte) 0xfa, (byte) 0xc0, (byte) 0xb4, (byte) 0x6c, (byte) 0xc9, (byte) 0x02,
+            (byte) 0x81, (byte) 0xc1, (byte) 0x00, (byte) 0xf3, (byte) 0x17, (byte) 0xd9,
+            (byte) 0x48, (byte) 0x17, (byte) 0x87, (byte) 0x84, (byte) 0x16, (byte) 0xea,
+            (byte) 0x2d, (byte) 0x31, (byte) 0x1b, (byte) 0xce, (byte) 0xec, (byte) 0xaf,
+            (byte) 0xdc, (byte) 0x6b, (byte) 0xaf, (byte) 0xc8, (byte) 0xf1, (byte) 0x40,
+            (byte) 0xa7, (byte) 0x4f, (byte) 0xef, (byte) 0x48, (byte) 0x08, (byte) 0x5e,
+            (byte) 0x9a, (byte) 0xd1, (byte) 0xc0, (byte) 0xb1, (byte) 0xfe, (byte) 0xe7,
+            (byte) 0x03, (byte) 0xd5, (byte) 0x96, (byte) 0x01, (byte) 0xe8, (byte) 0x40,
+            (byte) 0xca, (byte) 0x78, (byte) 0xcb, (byte) 0xb3, (byte) 0x28, (byte) 0x1a,
+            (byte) 0xf0, (byte) 0xe5, (byte) 0xf6, (byte) 0x46, (byte) 0xef, (byte) 0xcd,
+            (byte) 0x1a, (byte) 0x0f, (byte) 0x13, (byte) 0x2d, (byte) 0x38, (byte) 0xf8,
+            (byte) 0xf7, (byte) 0x88, (byte) 0x21, (byte) 0x15, (byte) 0xce, (byte) 0x48,
+            (byte) 0xf4, (byte) 0x92, (byte) 0x7e, (byte) 0x9b, (byte) 0x2e, (byte) 0x2f,
+            (byte) 0x22, (byte) 0x3e, (byte) 0x5c, (byte) 0x67, (byte) 0xd7, (byte) 0x58,
+            (byte) 0xf6, (byte) 0xef, (byte) 0x1f, (byte) 0xb4, (byte) 0x04, (byte) 0xc7,
+            (byte) 0xfd, (byte) 0x8c, (byte) 0x4e, (byte) 0x27, (byte) 0x9e, (byte) 0xb9,
+            (byte) 0xef, (byte) 0x0f, (byte) 0xf7, (byte) 0x4a, (byte) 0xc2, (byte) 0xf4,
+            (byte) 0x64, (byte) 0x6b, (byte) 0xe0, (byte) 0xfb, (byte) 0xe3, (byte) 0x45,
+            (byte) 0xd5, (byte) 0x37, (byte) 0xa0, (byte) 0x2a, (byte) 0xc6, (byte) 0xf3,
+            (byte) 0xf6, (byte) 0xcc, (byte) 0xb5, (byte) 0x94, (byte) 0xbf, (byte) 0x56,
+            (byte) 0xa0, (byte) 0x61, (byte) 0x36, (byte) 0x88, (byte) 0x35, (byte) 0xd5,
+            (byte) 0xa5, (byte) 0xad, (byte) 0x20, (byte) 0x48, (byte) 0xda, (byte) 0x70,
+            (byte) 0x35, (byte) 0xd9, (byte) 0x75, (byte) 0x66, (byte) 0xa5, (byte) 0xac,
+            (byte) 0x86, (byte) 0x7a, (byte) 0x75, (byte) 0x49, (byte) 0x88, (byte) 0x40,
+            (byte) 0xce, (byte) 0xb0, (byte) 0x6f, (byte) 0x57, (byte) 0x15, (byte) 0x54,
+            (byte) 0xd3, (byte) 0x2f, (byte) 0x11, (byte) 0x9b, (byte) 0xe3, (byte) 0x87,
+            (byte) 0xc8, (byte) 0x8d, (byte) 0x98, (byte) 0xc6, (byte) 0xe0, (byte) 0xbc,
+            (byte) 0x85, (byte) 0xb9, (byte) 0x04, (byte) 0x43, (byte) 0xa9, (byte) 0x41,
+            (byte) 0xce, (byte) 0x42, (byte) 0x1a, (byte) 0x57, (byte) 0x10, (byte) 0xd8,
+            (byte) 0xe4, (byte) 0x6a, (byte) 0x51, (byte) 0x10, (byte) 0x0a, (byte) 0xec,
+            (byte) 0xe4, (byte) 0x57, (byte) 0xc7, (byte) 0xee, (byte) 0xe9, (byte) 0xd6,
+            (byte) 0xcb, (byte) 0x3e, (byte) 0xba, (byte) 0xfa, (byte) 0xe9, (byte) 0x0e,
+            (byte) 0xed, (byte) 0x87, (byte) 0x04, (byte) 0x9a, (byte) 0x48, (byte) 0xba,
+            (byte) 0xaf, (byte) 0x08, (byte) 0xf5, (byte) 0x02, (byte) 0x81, (byte) 0xc1,
+            (byte) 0x00, (byte) 0xcb, (byte) 0x63, (byte) 0xd6, (byte) 0x54, (byte) 0xb6,
+            (byte) 0xf3, (byte) 0xf3, (byte) 0x8c, (byte) 0xf8, (byte) 0xd0, (byte) 0xd2,
+            (byte) 0x84, (byte) 0xc1, (byte) 0xf5, (byte) 0x12, (byte) 0xe0, (byte) 0x02,
+            (byte) 0x80, (byte) 0x42, (byte) 0x92, (byte) 0x4e, (byte) 0xa4, (byte) 0x5c,
+            (byte) 0xa5, (byte) 0x64, (byte) 0xec, (byte) 0xb7, (byte) 0xdc, (byte) 0xe0,
+            (byte) 0x2d, (byte) 0x5d, (byte) 0xac, (byte) 0x0e, (byte) 0x24, (byte) 0x48,
+            (byte) 0x13, (byte) 0x05, (byte) 0xe8, (byte) 0xff, (byte) 0x96, (byte) 0x93,
+            (byte) 0xba, (byte) 0x3c, (byte) 0x88, (byte) 0xcc, (byte) 0x80, (byte) 0xf9,
+            (byte) 0xdb, (byte) 0xa8, (byte) 0x4d, (byte) 0x86, (byte) 0x47, (byte) 0xc8,
+            (byte) 0xbf, (byte) 0x34, (byte) 0x2d, (byte) 0xda, (byte) 0xb6, (byte) 0x28,
+            (byte) 0xf0, (byte) 0x1e, (byte) 0xd2, (byte) 0x46, (byte) 0x0d, (byte) 0x6f,
+            (byte) 0x36, (byte) 0x8e, (byte) 0x84, (byte) 0xd8, (byte) 0xaf, (byte) 0xf7,
+            (byte) 0x69, (byte) 0x23, (byte) 0x77, (byte) 0xfb, (byte) 0xc5, (byte) 0x04,
+            (byte) 0x08, (byte) 0x18, (byte) 0xac, (byte) 0x85, (byte) 0x80, (byte) 0x87,
+            (byte) 0x1c, (byte) 0xfe, (byte) 0x8e, (byte) 0x5d, (byte) 0x00, (byte) 0x7f,
+            (byte) 0x5b, (byte) 0x33, (byte) 0xf5, (byte) 0xdf, (byte) 0x70, (byte) 0x81,
+            (byte) 0xad, (byte) 0x81, (byte) 0xf4, (byte) 0x5a, (byte) 0x37, (byte) 0x8a,
+            (byte) 0x79, (byte) 0x09, (byte) 0xc5, (byte) 0x55, (byte) 0xab, (byte) 0x58,
+            (byte) 0x7c, (byte) 0x47, (byte) 0xca, (byte) 0xa5, (byte) 0x80, (byte) 0x49,
+            (byte) 0x5f, (byte) 0x71, (byte) 0x83, (byte) 0xfb, (byte) 0x3b, (byte) 0x06,
+            (byte) 0xec, (byte) 0x75, (byte) 0x23, (byte) 0xc4, (byte) 0x32, (byte) 0xc7,
+            (byte) 0x18, (byte) 0xf6, (byte) 0x82, (byte) 0x95, (byte) 0x98, (byte) 0x39,
+            (byte) 0xf7, (byte) 0x92, (byte) 0x31, (byte) 0xc0, (byte) 0x89, (byte) 0xba,
+            (byte) 0xd4, (byte) 0xd4, (byte) 0x58, (byte) 0x4e, (byte) 0x38, (byte) 0x35,
+            (byte) 0x10, (byte) 0xb9, (byte) 0xf1, (byte) 0x27, (byte) 0xdc, (byte) 0xff,
+            (byte) 0xc7, (byte) 0xb2, (byte) 0xba, (byte) 0x1f, (byte) 0x27, (byte) 0xaf,
+            (byte) 0x99, (byte) 0xd5, (byte) 0xb0, (byte) 0x39, (byte) 0xe7, (byte) 0x43,
+            (byte) 0x88, (byte) 0xd3, (byte) 0xce, (byte) 0x38, (byte) 0xc2, (byte) 0x99,
+            (byte) 0x43, (byte) 0xfc, (byte) 0x8a, (byte) 0xe3, (byte) 0x60, (byte) 0x0d,
+            (byte) 0x0a, (byte) 0xb8, (byte) 0xc4, (byte) 0x29, (byte) 0xca, (byte) 0x0d,
+            (byte) 0x30, (byte) 0xaf, (byte) 0xca, (byte) 0xd0, (byte) 0xaa, (byte) 0x67,
+            (byte) 0xb1, (byte) 0xdd, (byte) 0xdb, (byte) 0x7a, (byte) 0x11, (byte) 0xad,
+            (byte) 0xeb, (byte) 0x02, (byte) 0x81, (byte) 0xc0, (byte) 0x71, (byte) 0xb8,
+            (byte) 0xcf, (byte) 0x72, (byte) 0x35, (byte) 0x67, (byte) 0xb5, (byte) 0x38,
+            (byte) 0x8f, (byte) 0x16, (byte) 0xd3, (byte) 0x29, (byte) 0x82, (byte) 0x35,
+            (byte) 0x21, (byte) 0xd4, (byte) 0x49, (byte) 0x20, (byte) 0x74, (byte) 0x2d,
+            (byte) 0xc0, (byte) 0xa4, (byte) 0x44, (byte) 0xf5, (byte) 0xd8, (byte) 0xc9,
+            (byte) 0xe9, (byte) 0x90, (byte) 0x1d, (byte) 0xde, (byte) 0x3a, (byte) 0xa6,
+            (byte) 0xd7, (byte) 0xe5, (byte) 0xe8, (byte) 0x4e, (byte) 0x83, (byte) 0xd7,
+            (byte) 0xe6, (byte) 0x2f, (byte) 0x92, (byte) 0x31, (byte) 0x21, (byte) 0x3f,
+            (byte) 0xfa, (byte) 0xd2, (byte) 0x85, (byte) 0x92, (byte) 0x1f, (byte) 0xff,
+            (byte) 0x61, (byte) 0x00, (byte) 0xf6, (byte) 0xda, (byte) 0x6e, (byte) 0xc6,
+            (byte) 0x7f, (byte) 0x5a, (byte) 0x35, (byte) 0x79, (byte) 0xdc, (byte) 0xdc,
+            (byte) 0xa3, (byte) 0x2e, (byte) 0x9f, (byte) 0x35, (byte) 0xd1, (byte) 0x5c,
+            (byte) 0xda, (byte) 0xb9, (byte) 0xf7, (byte) 0x58, (byte) 0x7d, (byte) 0x4f,
+            (byte) 0xb6, (byte) 0x13, (byte) 0xd7, (byte) 0x2c, (byte) 0x0a, (byte) 0xa8,
+            (byte) 0x4d, (byte) 0xf2, (byte) 0xe4, (byte) 0x67, (byte) 0x4f, (byte) 0x8b,
+            (byte) 0xa6, (byte) 0xca, (byte) 0x1a, (byte) 0xbb, (byte) 0x02, (byte) 0x63,
+            (byte) 0x8f, (byte) 0xb7, (byte) 0x46, (byte) 0xec, (byte) 0x7a, (byte) 0x8a,
+            (byte) 0x09, (byte) 0x0a, (byte) 0x45, (byte) 0x3a, (byte) 0x8d, (byte) 0xa8,
+            (byte) 0x83, (byte) 0x4b, (byte) 0x0a, (byte) 0xdb, (byte) 0x4b, (byte) 0x99,
+            (byte) 0xf3, (byte) 0x69, (byte) 0x95, (byte) 0xf0, (byte) 0xcf, (byte) 0xe9,
+            (byte) 0xf7, (byte) 0x67, (byte) 0xc9, (byte) 0x45, (byte) 0x18, (byte) 0x2f,
+            (byte) 0xf0, (byte) 0x5c, (byte) 0x90, (byte) 0xbd, (byte) 0xa6, (byte) 0x66,
+            (byte) 0x8c, (byte) 0xfe, (byte) 0x60, (byte) 0x5d, (byte) 0x6c, (byte) 0x27,
+            (byte) 0xec, (byte) 0xc1, (byte) 0x84, (byte) 0xb2, (byte) 0xa1, (byte) 0x97,
+            (byte) 0x9e, (byte) 0x16, (byte) 0x29, (byte) 0xa7, (byte) 0xe0, (byte) 0x38,
+            (byte) 0xa2, (byte) 0x36, (byte) 0x05, (byte) 0x5f, (byte) 0xda, (byte) 0x72,
+            (byte) 0x1a, (byte) 0x5f, (byte) 0xa8, (byte) 0x7d, (byte) 0x41, (byte) 0x35,
+            (byte) 0xf6, (byte) 0x4e, (byte) 0x0a, (byte) 0x88, (byte) 0x8e, (byte) 0x00,
+            (byte) 0x98, (byte) 0xa6, (byte) 0xca, (byte) 0xc1, (byte) 0xdf, (byte) 0x72,
+            (byte) 0x6c, (byte) 0xfe, (byte) 0x29, (byte) 0xbe, (byte) 0xa3, (byte) 0x9b,
+            (byte) 0x0b, (byte) 0x5c, (byte) 0x0b, (byte) 0x9d, (byte) 0xa7, (byte) 0x71,
+            (byte) 0xce, (byte) 0x04, (byte) 0xfa, (byte) 0xac, (byte) 0x01, (byte) 0x8d,
+            (byte) 0x52, (byte) 0xa0, (byte) 0x3d, (byte) 0xdd, (byte) 0x02, (byte) 0x81,
+            (byte) 0xc1, (byte) 0x00, (byte) 0xc1, (byte) 0xc0, (byte) 0x2e, (byte) 0xa9,
+            (byte) 0xee, (byte) 0xca, (byte) 0xff, (byte) 0xe4, (byte) 0xf8, (byte) 0x15,
+            (byte) 0xfd, (byte) 0xa5, (byte) 0x68, (byte) 0x1b, (byte) 0x2d, (byte) 0x4a,
+            (byte) 0xe6, (byte) 0x37, (byte) 0x06, (byte) 0xb3, (byte) 0xd7, (byte) 0x64,
+            (byte) 0xad, (byte) 0xb9, (byte) 0x05, (byte) 0x26, (byte) 0x97, (byte) 0x94,
+            (byte) 0x3a, (byte) 0x9e, (byte) 0x1c, (byte) 0xd0, (byte) 0xcd, (byte) 0x7b,
+            (byte) 0xf4, (byte) 0x88, (byte) 0xe2, (byte) 0xa5, (byte) 0x6d, (byte) 0xed,
+            (byte) 0x24, (byte) 0x77, (byte) 0x52, (byte) 0x39, (byte) 0x43, (byte) 0x0f,
+            (byte) 0x4e, (byte) 0x75, (byte) 0xd8, (byte) 0xa3, (byte) 0x59, (byte) 0x5a,
+            (byte) 0xc2, (byte) 0xba, (byte) 0x9a, (byte) 0x5b, (byte) 0x60, (byte) 0x31,
+            (byte) 0x0d, (byte) 0x58, (byte) 0x89, (byte) 0x13, (byte) 0xe8, (byte) 0x95,
+            (byte) 0xdd, (byte) 0xae, (byte) 0xcc, (byte) 0x1f, (byte) 0x73, (byte) 0x48,
+            (byte) 0x55, (byte) 0xd8, (byte) 0xfb, (byte) 0x67, (byte) 0xce, (byte) 0x18,
+            (byte) 0x85, (byte) 0x59, (byte) 0xad, (byte) 0x1f, (byte) 0x93, (byte) 0xe1,
+            (byte) 0xb7, (byte) 0x54, (byte) 0x80, (byte) 0x8e, (byte) 0x5f, (byte) 0xbc,
+            (byte) 0x1c, (byte) 0x96, (byte) 0x66, (byte) 0x2e, (byte) 0x40, (byte) 0x17,
+            (byte) 0x2e, (byte) 0x01, (byte) 0x7a, (byte) 0x7d, (byte) 0xaa, (byte) 0xff,
+            (byte) 0xa3, (byte) 0xd2, (byte) 0xdf, (byte) 0xe2, (byte) 0xf3, (byte) 0x54,
+            (byte) 0x51, (byte) 0xeb, (byte) 0xba, (byte) 0x7c, (byte) 0x2a, (byte) 0x22,
+            (byte) 0xc6, (byte) 0x42, (byte) 0xbc, (byte) 0xa1, (byte) 0x6c, (byte) 0xcf,
+            (byte) 0x73, (byte) 0x2e, (byte) 0x07, (byte) 0xfc, (byte) 0xf5, (byte) 0x67,
+            (byte) 0x25, (byte) 0xd0, (byte) 0xfa, (byte) 0xeb, (byte) 0xb4, (byte) 0xd4,
+            (byte) 0x19, (byte) 0xcc, (byte) 0x64, (byte) 0xa1, (byte) 0x2e, (byte) 0x78,
+            (byte) 0x45, (byte) 0xd9, (byte) 0x7f, (byte) 0x1b, (byte) 0x4c, (byte) 0x10,
+            (byte) 0x31, (byte) 0x44, (byte) 0xe8, (byte) 0xcc, (byte) 0xf9, (byte) 0x1b,
+            (byte) 0x87, (byte) 0x31, (byte) 0xd6, (byte) 0x69, (byte) 0x85, (byte) 0x4a,
+            (byte) 0x49, (byte) 0xf6, (byte) 0xb2, (byte) 0xe0, (byte) 0xb8, (byte) 0x98,
+            (byte) 0x3c, (byte) 0xf6, (byte) 0x78, (byte) 0x46, (byte) 0xc8, (byte) 0x3d,
+            (byte) 0x60, (byte) 0xc1, (byte) 0xaa, (byte) 0x2f, (byte) 0x28, (byte) 0xa1,
+            (byte) 0x14, (byte) 0x6b, (byte) 0x75, (byte) 0x4d, (byte) 0xb1, (byte) 0x3d,
+            (byte) 0x80, (byte) 0x49, (byte) 0x33, (byte) 0xfd, (byte) 0x71, (byte) 0xc0,
+            (byte) 0x13, (byte) 0x1e, (byte) 0x16, (byte) 0x69, (byte) 0x80, (byte) 0xa4,
+            (byte) 0x9c, (byte) 0xd7, (byte) 0x02, (byte) 0x81, (byte) 0xc1, (byte) 0x00,
+            (byte) 0x8c, (byte) 0x33, (byte) 0x2d, (byte) 0xd9, (byte) 0xf3, (byte) 0x42,
+            (byte) 0x4d, (byte) 0xca, (byte) 0x5e, (byte) 0x60, (byte) 0x14, (byte) 0x10,
+            (byte) 0xf6, (byte) 0xf3, (byte) 0x71, (byte) 0x15, (byte) 0x88, (byte) 0x54,
+            (byte) 0x84, (byte) 0x21, (byte) 0x04, (byte) 0xb1, (byte) 0xaf, (byte) 0x02,
+            (byte) 0x11, (byte) 0x7f, (byte) 0x42, (byte) 0x3e, (byte) 0x86, (byte) 0xcb,
+            (byte) 0x6c, (byte) 0xf5, (byte) 0x57, (byte) 0x78, (byte) 0x4a, (byte) 0x03,
+            (byte) 0x9b, (byte) 0x80, (byte) 0xc2, (byte) 0x04, (byte) 0x3a, (byte) 0x6b,
+            (byte) 0xb3, (byte) 0x30, (byte) 0x31, (byte) 0x7e, (byte) 0xc3, (byte) 0x89,
+            (byte) 0x09, (byte) 0x4e, (byte) 0x86, (byte) 0x59, (byte) 0x41, (byte) 0xb5,
+            (byte) 0xae, (byte) 0xd5, (byte) 0xc6, (byte) 0x38, (byte) 0xbc, (byte) 0xd7,
+            (byte) 0xd7, (byte) 0x8e, (byte) 0xa3, (byte) 0x1a, (byte) 0xde, (byte) 0x32,
+            (byte) 0xad, (byte) 0x8d, (byte) 0x15, (byte) 0x81, (byte) 0xfe, (byte) 0xac,
+            (byte) 0xbd, (byte) 0xd0, (byte) 0xca, (byte) 0xbc, (byte) 0xd8, (byte) 0x6a,
+            (byte) 0xe1, (byte) 0xfe, (byte) 0xda, (byte) 0xc4, (byte) 0xd8, (byte) 0x62,
+            (byte) 0x71, (byte) 0x20, (byte) 0xa3, (byte) 0xd3, (byte) 0x06, (byte) 0x11,
+            (byte) 0xa9, (byte) 0x53, (byte) 0x7a, (byte) 0x44, (byte) 0x89, (byte) 0x3d,
+            (byte) 0x28, (byte) 0x5e, (byte) 0x7d, (byte) 0xf0, (byte) 0x60, (byte) 0xeb,
+            (byte) 0xb5, (byte) 0xdf, (byte) 0xed, (byte) 0x4f, (byte) 0x6d, (byte) 0x05,
+            (byte) 0x59, (byte) 0x06, (byte) 0xb0, (byte) 0x62, (byte) 0x50, (byte) 0x1c,
+            (byte) 0xb7, (byte) 0x2c, (byte) 0x44, (byte) 0xa4, (byte) 0x49, (byte) 0xf8,
+            (byte) 0x4f, (byte) 0x4b, (byte) 0xab, (byte) 0x71, (byte) 0x5b, (byte) 0xcb,
+            (byte) 0x31, (byte) 0x10, (byte) 0x41, (byte) 0xe0, (byte) 0x1a, (byte) 0x15,
+            (byte) 0xdc, (byte) 0x4c, (byte) 0x5d, (byte) 0x4f, (byte) 0x62, (byte) 0x83,
+            (byte) 0xa4, (byte) 0x80, (byte) 0x06, (byte) 0x36, (byte) 0xba, (byte) 0xc9,
+            (byte) 0xe2, (byte) 0xa4, (byte) 0x11, (byte) 0x98, (byte) 0x6b, (byte) 0x4c,
+            (byte) 0xe9, (byte) 0x90, (byte) 0x55, (byte) 0x18, (byte) 0xde, (byte) 0xe1,
+            (byte) 0x42, (byte) 0x38, (byte) 0x28, (byte) 0xa3, (byte) 0x54, (byte) 0x56,
+            (byte) 0x31, (byte) 0xaf, (byte) 0x5a, (byte) 0xd6, (byte) 0xf0, (byte) 0x26,
+            (byte) 0xe0, (byte) 0x7a, (byte) 0xd9, (byte) 0x6c, (byte) 0x64, (byte) 0xca,
+            (byte) 0x5d, (byte) 0x6d, (byte) 0x3d, (byte) 0x9a, (byte) 0xfe, (byte) 0x36,
+            (byte) 0x93, (byte) 0x9e, (byte) 0x62, (byte) 0x94, (byte) 0xc6, (byte) 0x07,
+            (byte) 0x83, (byte) 0x96, (byte) 0xd6, (byte) 0x27, (byte) 0xa6, (byte) 0xd8
+    };
+    public static final PrivateKey CLIENT_SUITE_B_RSA3072_KEY =
+            loadPrivateKey("RSA", CLIENT_SUITE_B_RSA3072_KEY_DATA);
+
+    private static final String CLIENT_SUITE_B_ECDSA_CERT_STRING =
+            "-----BEGIN CERTIFICATE-----\n"
+                    + "MIIB9zCCAX4CFDpfSZh3AH07BEfGWuMDa7Ynz6y+MAoGCCqGSM49BAMDMF4xCzAJ\n"
+                    + "BgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDTVRWMRAwDgYDVQQKDAdB\n"
+                    + "bmRyb2lkMQ4wDAYDVQQLDAVXaS1GaTESMBAGA1UEAwwJdW5pdGVzdENBMB4XDTIw\n"
+                    + "MDcyMTAyMjk1MFoXDTMwMDUzMDAyMjk1MFowYjELMAkGA1UEBhMCVVMxCzAJBgNV\n"
+                    + "BAgMAkNBMQwwCgYDVQQHDANNVFYxEDAOBgNVBAoMB0FuZHJvaWQxDjAMBgNVBAsM\n"
+                    + "BVdpLUZpMRYwFAYDVQQDDA11bml0ZXN0Q2xpZW50MHYwEAYHKoZIzj0CAQYFK4EE\n"
+                    + "ACIDYgAEhxhVJ7dcSqrto0X+dgRxtd8BWG8cWmPjBji3MIxDLfpcMDoIB84ae1Ew\n"
+                    + "gJn4YUYHrWsUDiVNihv8j7a/Ol1qcIY2ybH7tbezefLmagqA4vXEUXZXoUyL4ZNC\n"
+                    + "DWcdw6LrMAoGCCqGSM49BAMDA2cAMGQCMH4aP73HrriRUJRguiuRic+X4Cqj/7YQ\n"
+                    + "ueJmP87KF92/thhoQ9OrRo8uJITPmNDswwIwP2Q1AZCSL4BI9dYrqu07Ar+pSkXE\n"
+                    + "R7oOqGdZR+d/MvXcFSrbIaLKEoHXmQamIHLe\n"
+                    + "-----END CERTIFICATE-----\n";
+    public static final X509Certificate CLIENT_SUITE_B_ECDSA_CERT =
+            loadCertificate(CLIENT_SUITE_B_ECDSA_CERT_STRING);
+
+    private static final byte[] CLIENT_SUITE_B_ECC_KEY_DATA = new byte[]{
+            (byte) 0x30, (byte) 0x81, (byte) 0xb6, (byte) 0x02, (byte) 0x01, (byte) 0x00,
+            (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x07, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, (byte) 0x06,
+            (byte) 0x05, (byte) 0x2b, (byte) 0x81, (byte) 0x04, (byte) 0x00, (byte) 0x22,
+            (byte) 0x04, (byte) 0x81, (byte) 0x9e, (byte) 0x30, (byte) 0x81, (byte) 0x9b,
+            (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0x30, (byte) 0xea,
+            (byte) 0x6c, (byte) 0x4b, (byte) 0x6d, (byte) 0x43, (byte) 0xf9, (byte) 0x6c,
+            (byte) 0x91, (byte) 0xdc, (byte) 0x2d, (byte) 0x6e, (byte) 0x87, (byte) 0x4f,
+            (byte) 0x0a, (byte) 0x0b, (byte) 0x97, (byte) 0x25, (byte) 0x1c, (byte) 0x79,
+            (byte) 0xa2, (byte) 0x07, (byte) 0xdc, (byte) 0x94, (byte) 0xc2, (byte) 0xee,
+            (byte) 0x64, (byte) 0x51, (byte) 0x6d, (byte) 0x4e, (byte) 0x35, (byte) 0x1c,
+            (byte) 0x22, (byte) 0x2f, (byte) 0xc0, (byte) 0xea, (byte) 0x09, (byte) 0x47,
+            (byte) 0x3e, (byte) 0xb9, (byte) 0xb6, (byte) 0xb8, (byte) 0x83, (byte) 0x9e,
+            (byte) 0xed, (byte) 0x59, (byte) 0xe5, (byte) 0xe7, (byte) 0x0f, (byte) 0xa1,
+            (byte) 0x64, (byte) 0x03, (byte) 0x62, (byte) 0x00, (byte) 0x04, (byte) 0x87,
+            (byte) 0x18, (byte) 0x55, (byte) 0x27, (byte) 0xb7, (byte) 0x5c, (byte) 0x4a,
+            (byte) 0xaa, (byte) 0xed, (byte) 0xa3, (byte) 0x45, (byte) 0xfe, (byte) 0x76,
+            (byte) 0x04, (byte) 0x71, (byte) 0xb5, (byte) 0xdf, (byte) 0x01, (byte) 0x58,
+            (byte) 0x6f, (byte) 0x1c, (byte) 0x5a, (byte) 0x63, (byte) 0xe3, (byte) 0x06,
+            (byte) 0x38, (byte) 0xb7, (byte) 0x30, (byte) 0x8c, (byte) 0x43, (byte) 0x2d,
+            (byte) 0xfa, (byte) 0x5c, (byte) 0x30, (byte) 0x3a, (byte) 0x08, (byte) 0x07,
+            (byte) 0xce, (byte) 0x1a, (byte) 0x7b, (byte) 0x51, (byte) 0x30, (byte) 0x80,
+            (byte) 0x99, (byte) 0xf8, (byte) 0x61, (byte) 0x46, (byte) 0x07, (byte) 0xad,
+            (byte) 0x6b, (byte) 0x14, (byte) 0x0e, (byte) 0x25, (byte) 0x4d, (byte) 0x8a,
+            (byte) 0x1b, (byte) 0xfc, (byte) 0x8f, (byte) 0xb6, (byte) 0xbf, (byte) 0x3a,
+            (byte) 0x5d, (byte) 0x6a, (byte) 0x70, (byte) 0x86, (byte) 0x36, (byte) 0xc9,
+            (byte) 0xb1, (byte) 0xfb, (byte) 0xb5, (byte) 0xb7, (byte) 0xb3, (byte) 0x79,
+            (byte) 0xf2, (byte) 0xe6, (byte) 0x6a, (byte) 0x0a, (byte) 0x80, (byte) 0xe2,
+            (byte) 0xf5, (byte) 0xc4, (byte) 0x51, (byte) 0x76, (byte) 0x57, (byte) 0xa1,
+            (byte) 0x4c, (byte) 0x8b, (byte) 0xe1, (byte) 0x93, (byte) 0x42, (byte) 0x0d,
+            (byte) 0x67, (byte) 0x1d, (byte) 0xc3, (byte) 0xa2, (byte) 0xeb
+    };
+    public static final PrivateKey CLIENT_SUITE_B_ECC_KEY =
+            loadPrivateKey("EC", CLIENT_SUITE_B_ECC_KEY_DATA);
+
     private static X509Certificate loadCertificate(String blob) {
         try {
             final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
@@ -255,9 +662,9 @@
         }
     }
 
-    private static PrivateKey loadPrivateRSAKey(byte[] fakeKey) {
+    private static PrivateKey loadPrivateKey(String algorithm, byte[] fakeKey) {
         try {
-            KeyFactory kf = KeyFactory.getInstance("RSA");
+            KeyFactory kf = KeyFactory.getInstance(algorithm);
             return kf.generatePrivate(new PKCS8EncodedKeySpec(fakeKey));
         } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
             return null;
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 5516f43..4a35868 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -44,6 +44,7 @@
     public static final long TEST_TSF = 04660l;
     public static final @WifiAnnotations.WifiStandard int TEST_WIFI_STANDARD =
             ScanResult.WIFI_STANDARD_11AC;
+    public static final String TEST_IFACE_NAME = "test_ifname";
 
     /**
      * Frequency to channel map. This include some frequencies used outside the US.
@@ -219,7 +220,7 @@
                 + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "
                 + "standard: 11ac, "
                 + "80211mcResponder: is not supported, "
-                + "Radio Chain Infos: null", scanResult.toString());
+                + "Radio Chain Infos: null, interface name: test_ifname", scanResult.toString());
     }
 
     /**
@@ -242,7 +243,8 @@
                 + "standard: 11ac, "
                 + "80211mcResponder: is not supported, "
                 + "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, "
-                + "RadioChainInfo: id=1, level=-54]", scanResult.toString());
+                + "RadioChainInfo: id=1, level=-54], interface name: test_ifname",
+                scanResult.toString());
     }
 
     /**
@@ -283,6 +285,8 @@
         result.frequency = TEST_FREQUENCY;
         result.timestamp = TEST_TSF;
         result.setWifiStandard(TEST_WIFI_STANDARD);
+        result.ifaceName = TEST_IFACE_NAME;
+
         return result;
     }
 
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index a7b6765..62220a6 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -22,6 +22,7 @@
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OWE;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_PSK;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_SAE;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_WAPI_CERT;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_WAPI_PSK;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -537,4 +538,52 @@
         configuration.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B);
         assertFalse(configuration.needsPreSharedKey());
     }
+
+    @Test
+    public void testGetAuthType() throws Exception {
+        WifiConfiguration configuration = new WifiConfiguration();
+
+        configuration.setSecurityParams(SECURITY_TYPE_PSK);
+        assertEquals(KeyMgmt.WPA_PSK, configuration.getAuthType());
+
+        configuration.setSecurityParams(SECURITY_TYPE_SAE);
+        assertEquals(KeyMgmt.SAE, configuration.getAuthType());
+
+        configuration.setSecurityParams(SECURITY_TYPE_WAPI_PSK);
+        assertEquals(KeyMgmt.WAPI_PSK, configuration.getAuthType());
+
+        configuration.setSecurityParams(SECURITY_TYPE_OPEN);
+        assertEquals(KeyMgmt.NONE, configuration.getAuthType());
+
+        configuration.setSecurityParams(SECURITY_TYPE_OWE);
+        assertEquals(KeyMgmt.OWE, configuration.getAuthType());
+
+        configuration.setSecurityParams(SECURITY_TYPE_EAP);
+        assertEquals(KeyMgmt.WPA_EAP, configuration.getAuthType());
+
+        configuration.setSecurityParams(SECURITY_TYPE_EAP_SUITE_B);
+        assertEquals(KeyMgmt.SUITE_B_192, configuration.getAuthType());
+
+        configuration.setSecurityParams(SECURITY_TYPE_WAPI_CERT);
+        assertEquals(KeyMgmt.WAPI_CERT, configuration.getAuthType());
+    }
+
+    @Test (expected = IllegalStateException.class)
+    public void testGetAuthTypeFailure1() throws Exception {
+        WifiConfiguration configuration = new WifiConfiguration();
+
+        configuration.setSecurityParams(SECURITY_TYPE_PSK);
+        configuration.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+        configuration.getAuthType();
+    }
+
+    @Test (expected = IllegalStateException.class)
+    public void testGetAuthTypeFailure2() throws Exception {
+        WifiConfiguration configuration = new WifiConfiguration();
+
+        configuration.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+        configuration.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+        configuration.allowedKeyManagement.set(KeyMgmt.SAE);
+        configuration.getAuthType();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index fc0ef46..6f47f3d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -22,6 +22,8 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
@@ -35,6 +37,8 @@
 
 import org.junit.Test;
 
+import java.security.cert.X509Certificate;
+
 /**
  * Unit tests for {@link android.net.wifi.WifiNetworkSpecifier}.
  */
@@ -45,6 +49,7 @@
     private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
     private static final String TEST_BSSID = "12:12:12:12:12:12";
     private static final String TEST_PRESHARED_KEY = "\"Test123\"";
+    private static final String TEST_DOMAIN_SUFFIX_MATCH = "domainSuffixMatch";
 
     /**
      * Validate correctness of WifiNetworkSpecifier object created by
@@ -135,6 +140,106 @@
                 wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getPhase2Method());
     }
 
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0);
+        enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+        NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa3EnterpriseConfig(enterpriseConfig)
+                .build();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+        assertFalse(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+        assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+        assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit RSA SuiteB network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3SuiteBRsaEapNetwork() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+        enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY,
+                new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+
+        enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+        NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa3EnterpriseConfig(enterpriseConfig)
+                .build();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.GCMP_256));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupManagementCiphers
+                .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+        assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+        assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit ECC SuiteB network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3SuiteBEccEapNetwork() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_ECDSA_CERT);
+        enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_ECC_KEY,
+                new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_ECDSA_CERT});
+
+        enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+        NetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa3EnterpriseConfig(enterpriseConfig)
+                .build();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals("\"" + TEST_SSID + "\"", wifiNetworkSpecifier.wifiConfiguration.SSID);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.GCMP_256));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupManagementCiphers
+                .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.requirePmf);
+        assertNull(wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+        assertNotNull(wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig);
+    }
 
     /**
      * Ensure {@link WifiNetworkSpecifier.Builder#setSsid(String)} throws an exception
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 3744a51..abb9ce6 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -32,6 +32,8 @@
 
 import org.junit.Test;
 
+import java.security.cert.X509Certificate;
+
 /**
  * Unit tests for {@link android.net.wifi.WifiNetworkSuggestion}.
  */
@@ -212,16 +214,14 @@
         assertNull(suggestion.getEnterpriseConfig());
     }
 
-
     /**
      * Validate correctness of WifiNetworkSuggestion object created by
-     * {@link WifiNetworkSuggestion.Builder#build()} for SuiteB network.
+     * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise network.
      */
     @Test
     public void testWifiNetworkSuggestionBuilderForWpa3EapNetwork() {
         WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
         enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
-        enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
         enterpriseConfig.setCaCertificate(FakeKeys.CA_CERT0);
         enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
 
@@ -232,6 +232,78 @@
 
         assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
         assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+        assertFalse(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(suggestion.wifiConfiguration.requirePmf);
+        assertNull(suggestion.wifiConfiguration.preSharedKey);
+        // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
+        // here.
+        assertTrue(suggestion.isUserAllowedToManuallyConnect);
+        assertTrue(suggestion.isInitialAutoJoinEnabled);
+        assertNotNull(suggestion.getEnterpriseConfig());
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit RSA SuiteB network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3SuiteBRsaEapNetwork() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_RSA3072_CERT);
+        enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_RSA3072_KEY,
+                new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_RSA3072_CERT});
+
+        enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa3EnterpriseConfig(enterpriseConfig)
+                .build();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
+        assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.GCMP_256));
+        assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers
+                .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
+        assertTrue(suggestion.wifiConfiguration.requirePmf);
+        assertNull(suggestion.wifiConfiguration.preSharedKey);
+        // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
+        // here.
+        assertTrue(suggestion.isUserAllowedToManuallyConnect);
+        assertTrue(suggestion.isInitialAutoJoinEnabled);
+        assertNotNull(suggestion.getEnterpriseConfig());
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkSuggestion.Builder#build()} for WPA3-Enterprise 192-bit ECC SuiteB network.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpa3SuiteBEccEapNetwork() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setCaCertificate(FakeKeys.CA_SUITE_B_ECDSA_CERT);
+        enterpriseConfig.setClientKeyEntryWithCertificateChain(FakeKeys.CLIENT_SUITE_B_ECC_KEY,
+                new X509Certificate[] {FakeKeys.CLIENT_SUITE_B_ECDSA_CERT});
+
+        enterpriseConfig.setDomainSuffixMatch(TEST_DOMAIN_SUFFIX_MATCH);
+
+        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setWpa3EnterpriseConfig(enterpriseConfig)
+                .build();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
                 .get(WifiConfiguration.KeyMgmt.SUITE_B_192));
         assertTrue(suggestion.wifiConfiguration.allowedGroupCiphers
                 .get(WifiConfiguration.GroupCipher.GCMP_256));
@@ -610,6 +682,33 @@
     }
 
     /**
+     * Verify that the macRandomizationSetting defaults to RANDOMIZATION_ENHANCED and could be set
+     * to RANDOMIZATION_PERSISTENT.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderSetMacRandomization() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .build();
+        assertEquals(WifiConfiguration.RANDOMIZATION_ENHANCED,
+                suggestion.wifiConfiguration.macRandomizationSetting);
+
+        suggestion = new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setIsEnhancedMacRandomizationEnabled(false)
+                .build();
+        assertEquals(WifiConfiguration.RANDOMIZATION_PERSISTENT,
+                suggestion.wifiConfiguration.macRandomizationSetting);
+
+        suggestion = new WifiNetworkSuggestion.Builder()
+                .setSsid(TEST_SSID)
+                .setIsEnhancedMacRandomizationEnabled(true)
+                .build();
+        assertEquals(WifiConfiguration.RANDOMIZATION_ENHANCED,
+                suggestion.wifiConfiguration.macRandomizationSetting);
+    }
+
+    /**
      * Check that parcel marshalling/unmarshalling works
      */
     @Test
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 43d728b..5fe0cb4 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -146,6 +146,15 @@
         verify(mockAwareService).getCharacteristics();
     }
 
+    /**
+     * Validate pass-through of isDeviceAttached() API.
+     */
+    @Test
+    public void testIsAttached() throws Exception {
+        mDut.isDeviceAttached();
+        verify(mockAwareService).isDeviceAttached();
+    }
+
     /*
      * WifiAwareEventCallbackProxy Tests
      */
@@ -360,12 +369,18 @@
                 eq(publishConfig));
         inOrder.verify(mockSessionCallback).onSessionConfigFailed();
 
-        // (5) terminate
+        // (5) discovery session is no longer visible
+        sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+        assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
+
+        // (6) terminate
         publishSession.getValue().close();
         mMockLooper.dispatchAll();
         inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
 
-        // (6) try an update (nothing)
+        // (7) try an update (nothing)
         publishSession.getValue().updatePublish(publishConfig);
         mMockLooper.dispatchAll();
 
@@ -502,12 +517,18 @@
                 eq(subscribeConfig));
         inOrder.verify(mockSessionCallback).onSessionConfigFailed();
 
-        // (5) terminate
+        // (5) discovery session is no longer visible
+        sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+        assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
+
+        // (6) terminate
         subscribeSession.getValue().close();
         mMockLooper.dispatchAll();
         inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
 
-        // (6) try an update (nothing)
+        // (7) try an update (nothing)
         subscribeSession.getValue().updateSubscribe(subscribeConfig);
         mMockLooper.dispatchAll();