Merge "Add userId param to AccountManagerService.getApplicationLabel."
diff --git a/Android.bp b/Android.bp
index e1cb037..82da1e6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -154,6 +154,7 @@
         "framework-scheduling.stubs.module_lib",
         "framework-sdkextensions.stubs.module_lib",
         "framework-statsd.stubs.module_lib",
+        "framework-supplementalprocess.stubs.module_lib",
         "framework-tethering.stubs.module_lib",
         "framework-uwb.stubs.module_lib",
         "framework-wifi.stubs.module_lib",
@@ -177,6 +178,7 @@
         "framework-scheduling.impl",
         "framework-sdkextensions.impl",
         "framework-statsd.impl",
+        "framework-supplementalprocess.impl",
         "framework-tethering.impl",
         "framework-uwb.impl",
         "framework-wifi.impl",
@@ -441,11 +443,8 @@
         "core/java/android/util/LocalLog.java",
         "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/IndentingPrintWriter.java",
-        "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/MessageUtils.java",
         "core/java/com/android/internal/util/RingBufferIndices.java",
-        "core/java/com/android/internal/util/State.java",
-        "core/java/com/android/internal/util/StateMachine.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
         "core/java/com/android/internal/util/TokenBucket.java",
     ],
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 31f7f6e..7a4ef2a 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -126,6 +126,7 @@
         ":framework-scheduling-sources",
         ":framework-sdkextensions-sources",
         ":framework-statsd-sources",
+        ":framework-supplementalprocess-sources",
         ":framework-tethering-srcs",
         ":framework-uwb-updatable-sources",
         ":framework-wifi-updatable-sources",
@@ -172,6 +173,7 @@
         ":framework-scheduling{.public.stubs.source}",
         ":framework-sdkextensions{.public.stubs.source}",
         ":framework-statsd{.public.stubs.source}",
+        ":framework-supplementalprocess{.public.stubs.source}",
         ":framework-tethering{.public.stubs.source}",
         ":framework-uwb{.public.stubs.source}",
         ":framework-wifi{.public.stubs.source}",
@@ -211,6 +213,7 @@
         ":framework-scheduling{.public.annotations.zip}",
         ":framework-sdkextensions{.public.annotations.zip}",
         ":framework-statsd{.public.annotations.zip}",
+        ":framework-supplementalprocess{.public.annotations.zip}",
         ":framework-tethering{.public.annotations.zip}",
         ":framework-uwb{.public.annotations.zip}",
         ":framework-wifi{.public.annotations.zip}",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index cc118f3..b6fd708 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -250,6 +250,7 @@
     "framework-scheduling.stubs",
     "framework-sdkextensions.stubs",
     "framework-statsd.stubs",
+    "framework-supplementalprocess.stubs",
     "framework-tethering.stubs",
     "framework-uwb.stubs",
     "framework-wifi.stubs",
@@ -271,6 +272,7 @@
     "framework-scheduling.stubs.system",
     "framework-sdkextensions.stubs.system",
     "framework-statsd.stubs.system",
+    "framework-supplementalprocess.stubs",
     "framework-tethering.stubs.system",
     "framework-uwb.stubs.system",
     "framework-wifi.stubs.system",
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 31a5a050..392df73 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -490,8 +490,8 @@
      * holding the AlarmManagerService.mLock lock.
      */
     @VisibleForTesting
-    final class Constants extends ContentObserver
-            implements DeviceConfig.OnPropertiesChangedListener {
+    final class Constants implements DeviceConfig.OnPropertiesChangedListener,
+            EconomyManagerInternal.TareStateChangeListener {
         @VisibleForTesting
         static final int MAX_EXACT_ALARM_DENY_LIST_SIZE = 250;
 
@@ -695,7 +695,6 @@
         private int mVersion = 0;
 
         Constants(Handler handler) {
-            super(handler);
             updateAllowWhileIdleWhitelistDurationLocked();
             for (int i = 0; i < APP_STANDBY_QUOTAS.length; i++) {
                 APP_STANDBY_QUOTAS[i] = DEFAULT_APP_STANDBY_QUOTAS[i];
@@ -709,11 +708,12 @@
         }
 
         public void start() {
-            mInjector.registerContentObserver(this,
-                    Settings.Global.getUriFor(Settings.Global.ENABLE_TARE));
             mInjector.registerDeviceConfigListener(this);
+            final EconomyManagerInternal economyManagerInternal =
+                    LocalServices.getService(EconomyManagerInternal.class);
+            economyManagerInternal.registerTareStateChangeListener(this);
             onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
-            updateTareSettings();
+            updateTareSettings(economyManagerInternal.isEnabled());
         }
 
         public void updateAllowWhileIdleWhitelistDurationLocked() {
@@ -886,15 +886,12 @@
         }
 
         @Override
-        public void onChange(boolean selfChange) {
-            updateTareSettings();
+        public void onTareEnabledStateChanged(boolean isTareEnabled) {
+            updateTareSettings(isTareEnabled);
         }
 
-        private void updateTareSettings() {
+        private void updateTareSettings(boolean isTareEnabled) {
             synchronized (mLock) {
-                final boolean isTareEnabled = Settings.Global.getInt(
-                        getContext().getContentResolver(),
-                        Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
                 if (USE_TARE_POLICY != isTareEnabled) {
                     USE_TARE_POLICY = isTareEnabled;
                     final boolean changed = mAlarmStore.updateAlarmDeliveries(alarm -> {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 78140dc..714c90b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -43,7 +43,6 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -54,7 +53,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
@@ -118,6 +116,7 @@
 import com.android.server.job.restrictions.JobRestriction;
 import com.android.server.job.restrictions.ThermalStatusRestriction;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomyManagerInternal;
 import com.android.server.usage.AppStandbyInternal;
 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 import com.android.server.utils.quota.Categorizer;
@@ -356,41 +355,22 @@
     // (ScheduledJobStateChanged and JobStatusDumpProto).
     public static final int RESTRICTED_INDEX = 5;
 
-    private class ConstantsObserver extends ContentObserver
-            implements DeviceConfig.OnPropertiesChangedListener {
-        private final ContentResolver mContentResolver;
-
-        ConstantsObserver(Handler handler, Context context) {
-            super(handler);
-            mContentResolver = context.getContentResolver();
-        }
-
+    private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener,
+            EconomyManagerInternal.TareStateChangeListener {
         public void start() {
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     JobSchedulerBackgroundThread.getExecutor(), this);
-            mContentResolver.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
+            final EconomyManagerInternal economyManagerInternal =
+                    LocalServices.getService(EconomyManagerInternal.class);
+            economyManagerInternal.registerTareStateChangeListener(this);
             // Load all the constants.
             synchronized (mLock) {
-                mConstants.updateSettingsConstantsLocked(mContentResolver);
+                mConstants.updateTareSettingsLocked(economyManagerInternal.isEnabled());
             }
             onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
         }
 
         @Override
-        public void onChange(boolean selfChange) {
-            synchronized (mLock) {
-                if (mConstants.updateSettingsConstantsLocked(mContentResolver)) {
-                    for (int controller = 0; controller < mControllers.size(); controller++) {
-                        final StateController sc = mControllers.get(controller);
-                        sc.onConstantsUpdatedLocked();
-                    }
-                    onControllerStateChanged(null);
-                }
-            }
-        }
-
-        @Override
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
             boolean apiQuotaScheduleUpdated = false;
             boolean concurrencyUpdated = false;
@@ -465,6 +445,17 @@
                 }
             }
         }
+
+        @Override
+        public void onTareEnabledStateChanged(boolean isTareEnabled) {
+            if (mConstants.updateTareSettingsLocked(isTareEnabled)) {
+                for (int controller = 0; controller < mControllers.size(); controller++) {
+                    final StateController sc = mControllers.get(controller);
+                    sc.onConstantsUpdatedLocked();
+                }
+                onControllerStateChanged(null);
+            }
+        }
     }
 
     @VisibleForTesting
@@ -719,10 +710,8 @@
                             DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
         }
 
-        private boolean updateSettingsConstantsLocked(ContentResolver contentResolver) {
+        private boolean updateTareSettingsLocked(boolean isTareEnabled) {
             boolean changed = false;
-            final boolean isTareEnabled = Settings.Global.getInt(contentResolver,
-                    Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
             if (USE_TARE_POLICY != isTareEnabled) {
                 USE_TARE_POLICY = isTareEnabled;
                 changed = true;
@@ -1673,7 +1662,7 @@
 
         mHandler = new JobHandler(context.getMainLooper());
         mConstants = new Constants();
-        mConstantsObserver = new ConstantsObserver(mHandler, context);
+        mConstantsObserver = new ConstantsObserver();
         mJobSchedulerStub = new JobSchedulerStub();
 
         mConcurrencyManager = new JobConcurrencyManager(this);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
index 29aa946..630f1e7 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
@@ -119,6 +119,11 @@
                 boolean canAfford);
     }
 
+    /** Listener for various TARE state changes. */
+    interface TareStateChangeListener {
+        void onTareEnabledStateChanged(boolean isTareEnabled);
+    }
+
     /**
      * Return {@code true} if the app is able to pay for the anticipated actions.
      */
@@ -130,6 +135,9 @@
      */
     long getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill);
 
+    /** Returns true if TARE is enabled. */
+    boolean isEnabled();
+
     /**
      * Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the
      * indicated bill changes.
@@ -145,6 +153,16 @@
             @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill);
 
     /**
+     * Register a {@link TareStateChangeListener} to track when TARE's state changes.
+     */
+    void registerTareStateChangeListener(@NonNull TareStateChangeListener listener);
+
+    /**
+     * Unregister a {@link TareStateChangeListener} from being notified when TARE's state changes.
+     */
+    void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener);
+
+    /**
      * Note that an instantaneous event has occurred. The event must be specified in one of the
      * EconomicPolicies.
      *
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index ed11097..437a101 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -53,6 +53,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
@@ -68,11 +69,13 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArraySet;
 
 /**
  * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
@@ -169,6 +172,9 @@
     @GuardedBy("mPackageToUidCache")
     private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
 
+    private final CopyOnWriteArraySet<TareStateChangeListener> mStateChangeListeners =
+            new CopyOnWriteArraySet<>();
+
     /** List of packages that are "exempted" from battery restrictions. */
     // TODO(144864180): include userID
     @GuardedBy("mLock")
@@ -261,6 +267,7 @@
     private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
     private static final int MSG_PROCESS_USAGE_EVENT = 2;
     private static final int MSG_MAYBE_FORCE_RECLAIM = 3;
+    private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 4;
     private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
 
     /**
@@ -802,6 +809,13 @@
                 }
                 break;
 
+                case MSG_NOTIFY_STATE_CHANGE_LISTENERS: {
+                    for (TareStateChangeListener listener : mStateChangeListeners) {
+                        listener.onTareEnabledStateChanged(mIsEnabled);
+                    }
+                }
+                break;
+
                 case MSG_PROCESS_USAGE_EVENT: {
                     final int userId = msg.arg1;
                     final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
@@ -892,6 +906,16 @@
         }
 
         @Override
+        public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener) {
+            mStateChangeListeners.add(listener);
+        }
+
+        @Override
+        public void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener) {
+            mStateChangeListeners.remove(listener);
+        }
+
+        @Override
         public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) {
             if (!mIsEnabled) {
                 return true;
@@ -944,6 +968,11 @@
         }
 
         @Override
+        public boolean isEnabled() {
+            return mIsEnabled;
+        }
+
+        @Override
         public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
                 @Nullable String tag) {
             if (!mIsEnabled) {
@@ -980,7 +1009,10 @@
         }
     }
 
-    private class ConfigObserver extends ContentObserver {
+    private class ConfigObserver extends ContentObserver
+            implements DeviceConfig.OnPropertiesChangedListener {
+        private static final String KEY_DC_ENABLE_TARE = "enable_tare";
+
         private final ContentResolver mContentResolver;
 
         ConfigObserver(Handler handler, Context context) {
@@ -989,12 +1021,15 @@
         }
 
         public void start() {
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_TARE,
+                    TareHandlerThread.getExecutor(), this);
             mContentResolver.registerContentObserver(
                     Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
             mContentResolver.registerContentObserver(
                     Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this);
             mContentResolver.registerContentObserver(
                     Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this);
+            onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TARE));
             updateEnabledStatus();
         }
 
@@ -1008,9 +1043,31 @@
             }
         }
 
+        @Override
+        public void onPropertiesChanged(DeviceConfig.Properties properties) {
+            synchronized (mLock) {
+                for (String name : properties.getKeyset()) {
+                    if (name == null) {
+                        continue;
+                    }
+                    switch (name) {
+                        case KEY_DC_ENABLE_TARE:
+                            updateEnabledStatus();
+                            break;
+                    }
+                }
+            }
+        }
+
         private void updateEnabledStatus() {
+            // User setting should override DeviceConfig setting.
+            // NOTE: There's currently no way for a user to reset the value (via UI), so if a user
+            // manually toggles TARE via UI, we'll always defer to the user's current setting
+            // TODO: add a "reset" value if the user toggle is an issue
+            final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE,
+                    KEY_DC_ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE == 1);
             final boolean isTareEnabled = Settings.Global.getInt(mContentResolver,
-                    Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
+                    Settings.Global.ENABLE_TARE, isTareEnabledDC ? 1 : 0) == 1;
             if (mIsEnabled != isTareEnabled) {
                 mIsEnabled = isTareEnabled;
                 if (mIsEnabled) {
@@ -1018,6 +1075,7 @@
                 } else {
                     tearDownEverything();
                 }
+                mHandler.sendEmptyMessage(MSG_NOTIFY_STATE_CHANGE_LISTENERS);
             }
         }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
index 5b2b660..65ef8bf 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
@@ -17,10 +17,13 @@
 package com.android.server.tare;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Singleton thread for all of TARE.
  *
@@ -29,6 +32,7 @@
 final class TareHandlerThread extends HandlerThread {
 
     private static TareHandlerThread sInstance;
+    private static Executor sHandlerExecutor;
     private static Handler sHandler;
 
     private TareHandlerThread() {
@@ -42,6 +46,7 @@
             final Looper looper = sInstance.getLooper();
             looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -52,6 +57,14 @@
         return sInstance;
     }
 
+    /** Returns the singleton handler executor for TareHandlerThread */
+    public static Executor getExecutor() {
+        synchronized (TareHandlerThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
+
     /** Returns the singleton handler for TareHandlerThread. */
     public static Handler getHandler() {
         synchronized (TareHandlerThread.class) {
diff --git a/api/Android.bp b/api/Android.bp
index 1bc50bd..1ec1b3c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -106,6 +106,7 @@
         ":framework-scheduling{.public.api.txt}",
         ":framework-sdkextensions{.public.api.txt}",
         ":framework-statsd{.public.api.txt}",
+        ":framework-supplementalprocess{.public.api.txt}",
         ":framework-tethering{.public.api.txt}",
         ":framework-uwb{.public.api.txt}",
         ":framework-wifi{.public.api.txt}",
@@ -167,6 +168,7 @@
         ":framework-scheduling{.public.stubs.source}",
         ":framework-sdkextensions{.public.stubs.source}",
         ":framework-statsd{.public.stubs.source}",
+        ":framework-supplementalprocess{.public.stubs.source}",
         ":framework-tethering{.public.stubs.source}",
         ":framework-uwb{.public.stubs.source}",
         ":framework-wifi{.public.stubs.source}",
@@ -195,6 +197,7 @@
         ":framework-scheduling{.public.removed-api.txt}",
         ":framework-sdkextensions{.public.removed-api.txt}",
         ":framework-statsd{.public.removed-api.txt}",
+        ":framework-supplementalprocess{.public.removed-api.txt}",
         ":framework-tethering{.public.removed-api.txt}",
         ":framework-uwb{.public.removed-api.txt}",
         ":framework-wifi{.public.removed-api.txt}",
@@ -237,6 +240,7 @@
         ":framework-scheduling{.system.api.txt}",
         ":framework-sdkextensions{.system.api.txt}",
         ":framework-statsd{.system.api.txt}",
+        ":framework-supplementalprocess{.system.api.txt}",
         ":framework-tethering{.system.api.txt}",
         ":framework-uwb{.system.api.txt}",
         ":framework-wifi{.system.api.txt}",
@@ -297,6 +301,7 @@
         ":framework-scheduling{.system.removed-api.txt}",
         ":framework-sdkextensions{.system.removed-api.txt}",
         ":framework-statsd{.system.removed-api.txt}",
+        ":framework-supplementalprocess{.system.removed-api.txt}",
         ":framework-tethering{.system.removed-api.txt}",
         ":framework-uwb{.system.removed-api.txt}",
         ":framework-wifi{.system.removed-api.txt}",
@@ -339,6 +344,7 @@
         ":framework-scheduling{.module-lib.api.txt}",
         ":framework-sdkextensions{.module-lib.api.txt}",
         ":framework-statsd{.module-lib.api.txt}",
+        ":framework-supplementalprocess{.module-lib.api.txt}",
         ":framework-tethering{.module-lib.api.txt}",
         ":framework-uwb{.module-lib.api.txt}",
         ":framework-wifi{.module-lib.api.txt}",
@@ -401,6 +407,7 @@
         ":framework-scheduling{.module-lib.removed-api.txt}",
         ":framework-sdkextensions{.module-lib.removed-api.txt}",
         ":framework-statsd{.module-lib.removed-api.txt}",
+        ":framework-supplementalprocess{.module-lib.removed-api.txt}",
         ":framework-tethering{.module-lib.removed-api.txt}",
         ":framework-uwb{.module-lib.removed-api.txt}",
         ":framework-wifi{.module-lib.removed-api.txt}",
@@ -521,6 +528,7 @@
         ":framework-scheduling.stubs{.jar}",
         ":framework-sdkextensions.stubs{.jar}",
         ":framework-statsd.stubs{.jar}",
+        ":framework-supplementalprocess.stubs{.jar}",
         ":framework-tethering.stubs{.jar}",
         ":framework-uwb.stubs{.jar}",
         ":framework-wifi.stubs{.jar}",
diff --git a/boot/Android.bp b/boot/Android.bp
index d88e839..3273f2c 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -96,6 +96,10 @@
             module: "com.android.sdkext-bootclasspath-fragment",
         },
         {
+            apex: "com.android.supplementalprocess",
+            module: "com.android.supplementalprocess-bootclasspath-fragment",
+        },
+        {
             apex: "com.android.tethering",
             module: "com.android.tethering-bootclasspath-fragment",
         },
diff --git a/config/README.md b/config/README.md
index 5597ae2..450a5c6 100644
--- a/config/README.md
+++ b/config/README.md
@@ -1,7 +1,7 @@
 # Configuration files for ART compiling the framework
 
 * boot-image-profile.txt: A list of methods from the boot classpath to be compiled by dex2oat.
-  The order in the file is not relelvant.
+  The order in the file is not relevant.
 * boot-profile.txt: An ordered list of methods from the boot classpath to be compiled by
   the JIT in the order provided in the file. Used by JIT zygote, when on-device
   signing failed.
diff --git a/core/api/current.txt b/core/api/current.txt
index fe0a85d..5656c31 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -411,6 +411,7 @@
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canControlMagnification = 16844039; // 0x1010507
+    field public static final int canDisplayOnRemoteDevices;
     field public static final int canPauseRecording = 16844314; // 0x101061a
     field public static final int canPerformGestures = 16844045; // 0x101050d
     field public static final int canRecord = 16844060; // 0x101051c
@@ -31676,6 +31677,7 @@
     method public float readFloat();
     method public void readFloatArray(@NonNull float[]);
     method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
+    method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>);
     method public int readInt();
     method public void readIntArray(@NonNull int[]);
     method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>);
@@ -31685,6 +31687,7 @@
     method public long readLong();
     method public void readLongArray(@NonNull long[]);
     method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
+    method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
     method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
     method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
     method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
@@ -40937,6 +40940,7 @@
     method @NonNull public java.util.List<java.lang.Integer> getBands();
     method @NonNull public java.util.List<java.lang.String> getMccMncs();
     method public int getPriority();
+    method @NonNull public java.util.List<android.telephony.RadioAccessSpecifier> getRadioAccessSpecifiers();
     method public int getSubId();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR;
@@ -40945,6 +40949,14 @@
     field public static final int PRIORITY_MED = 2; // 0x2
   }
 
+  public static final class AvailableNetworkInfo.Builder {
+    ctor public AvailableNetworkInfo.Builder(int);
+    method @NonNull public android.telephony.AvailableNetworkInfo build();
+    method @NonNull public android.telephony.AvailableNetworkInfo.Builder setMccMncs(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.telephony.AvailableNetworkInfo.Builder setPriority(int);
+    method @NonNull public android.telephony.AvailableNetworkInfo.Builder setRadioAccessSpecifiers(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
+  }
+
   public final class BarringInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int);
@@ -41053,6 +41065,7 @@
     field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
     field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
     field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+    field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
     field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
     field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
     field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
@@ -41114,6 +41127,8 @@
     field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
     field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int";
+    field public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT = "esim_download_retry_backoff_timer_sec_int";
+    field public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT = "esim_max_download_retry_attempts_int";
     field public static final String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
     field public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
@@ -41216,6 +41231,7 @@
     field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
     field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
     field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+    field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
     field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
     field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
     field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
@@ -52256,6 +52272,7 @@
     method public boolean commitContent(@NonNull android.view.inputmethod.InputContentInfo, int, @Nullable android.os.Bundle);
     method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
     method public boolean commitText(CharSequence, int);
+    method public default boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
     method public boolean deleteSurroundingText(int, int);
     method public boolean deleteSurroundingTextInCodePoints(int, int);
     method public boolean endBatchEdit();
@@ -52275,7 +52292,9 @@
     method public boolean requestCursorUpdates(int);
     method public boolean sendKeyEvent(android.view.KeyEvent);
     method public boolean setComposingRegion(int, int);
+    method public default boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
     method public boolean setComposingText(CharSequence, int);
+    method public default boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
     method public default boolean setImeConsumesInput(boolean);
     method public boolean setSelection(int, int);
     method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
@@ -52491,6 +52510,21 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
   }
 
+  public final class TextAttribute implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.os.PersistableBundle getExtras();
+    method @NonNull public java.util.List<java.lang.String> getTextConversionSuggestions();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextAttribute> CREATOR;
+  }
+
+  public static final class TextAttribute.TextAttributeBuilder {
+    ctor public TextAttribute.TextAttributeBuilder();
+    method @NonNull public android.view.inputmethod.TextAttribute build();
+    method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setExtras(@NonNull android.os.PersistableBundle);
+    method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setTextConversionSuggestions(@NonNull java.util.List<java.lang.String>);
+  }
+
   public final class TextSnapshot {
     ctor public TextSnapshot(@NonNull android.view.inputmethod.SurroundingText, @IntRange(from=0xffffffff) int, @IntRange(from=0xffffffff) int, int);
     method @IntRange(from=0xffffffff) public int getCompositionEnd();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2647d90..d0a73bd 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14,6 +14,7 @@
     field public static final String ACCESS_MTP = "android.permission.ACCESS_MTP";
     field public static final String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
     field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
+    field public static final String ACCESS_PDB_STATE = "android.permission.ACCESS_PDB_STATE";
     field public static final String ACCESS_RCS_USER_CAPABILITY_EXCHANGE = "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE";
     field public static final String ACCESS_SHARED_LIBRARIES = "android.permission.ACCESS_SHARED_LIBRARIES";
     field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
@@ -85,6 +86,7 @@
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+    field public static final String CONFIGURE_INTERACT_ACROSS_PROFILES = "android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES";
     field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
     field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
     field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
@@ -121,6 +123,7 @@
     field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
     field public static final String INSTALL_DPC_PACKAGES = "android.permission.INSTALL_DPC_PACKAGES";
     field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM";
+    field public static final String INSTALL_EXISTING_PACKAGES = "com.android.permission.INSTALL_EXISTING_PACKAGES";
     field public static final String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
     field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE";
     field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
@@ -147,12 +150,14 @@
     field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
     field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
+    field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
     field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
     field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
+    field public static final String MANAGE_PROFILE_AND_DEVICE_OWNERS = "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS";
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
@@ -169,6 +174,7 @@
     field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
     field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
+    field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
     field public static final String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
     field public static final String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
     field public static final String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
@@ -247,6 +253,7 @@
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
     field public static final String REQUEST_COMPANION_PROFILE_APP_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING";
+    field public static final String REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION = "android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION";
     field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -300,6 +307,7 @@
     field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
     field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
     field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
+    field public static final String USE_COLORIZED_NOTIFICATIONS = "android.permission.USE_COLORIZED_NOTIFICATIONS";
     field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
     field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
     field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
@@ -369,6 +377,7 @@
     field public static final int config_defaultCallScreening = 17039398; // 0x1040026
     field public static final int config_defaultDialer = 17039395; // 0x1040023
     field public static final int config_defaultSms = 17039396; // 0x1040024
+    field public static final int config_deviceManager;
     field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
     field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
     field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d
@@ -949,7 +958,7 @@
     method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
     method @Nullable public CharSequence getDeviceOwnerOrganizationName();
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
@@ -967,7 +976,7 @@
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
     method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
-    method @Deprecated @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
     method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
     method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
@@ -2334,6 +2343,7 @@
 
   public final class AssociationRequest implements android.os.Parcelable {
     field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING) public static final String DEVICE_PROFILE_APP_STREAMING = "android.app.role.COMPANION_DEVICE_APP_STREAMING";
+    field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION) public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION = "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
   }
 
   public final class CompanionDeviceManager {
@@ -10462,7 +10472,7 @@
 package android.service.persistentdata {
 
   public class PersistentDataBlockManager {
-    method @RequiresPermission("android.permission.ACCESS_PDB_STATE") public int getDataBlockSize();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize();
     method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
     method public long getMaximumDataBlockSize();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 9a8a493..9cb9ddc 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -1,12 +1,4 @@
 // Signature format: 2.0
-package android {
-
-  public static final class Manifest.permission {
-    field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
-  }
-
-}
-
 package android.app {
 
   public class AppOpsManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 591fe55..5f87630 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -442,11 +442,11 @@
 
   public class DevicePolicyManager {
     method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
-    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void clearOrganizationId();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
     method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
     method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
     method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
-    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
     method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
     method public void forceUpdateUserSetupComplete(int);
     method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
@@ -455,18 +455,18 @@
     method public long getLastNetworkLogRetrievalTime();
     method public long getLastSecurityLogRetrievalTime();
     method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
-    method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
     method @NonNull public static String operationSafetyReasonToString(int);
     method @NonNull public static String operationToString(int);
-    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
-    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
-    method @RequiresPermission(allOf={"android.permission.MANAGE_DEVICE_ADMINS", android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
-    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
-    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
-    method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
+    method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
     field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
@@ -799,6 +799,7 @@
     field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f;
     field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
     field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
+    field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L
     field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
   }
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 737360d..6a0f5c7 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -241,6 +241,14 @@
      */
     public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID";
 
+    /**
+     * Extra passed on {@link Intent.ACTION_WALLPAPER_CHANGED} indicating if wallpaper was set from
+     * a foreground app.
+     * @hide
+     */
+    public static final String EXTRA_FROM_FOREGROUND_APP =
+            "android.service.wallpaper.extra.FROM_FOREGROUND_APP";
+
     // flags for which kind of wallpaper to act on
 
     /** @hide */
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 24d1248..7d1aabc 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -94,6 +94,21 @@
     public static final String DEVICE_PROFILE_APP_STREAMING =
             "android.app.role.COMPANION_DEVICE_APP_STREAMING";
 
+    /**
+     * Device profile: Android Automotive Projection
+     *
+     * Only applications that have been granted
+     * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION} are
+     * allowed to request to be associated with such devices.
+     *
+     * @see AssociationRequest.Builder#setDeviceProfile
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION)
+    @SystemApi
+    public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION =
+            "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
+
     /** @hide */
     @StringDef(value = { DEVICE_PROFILE_WATCH })
     public @interface DeviceProfile {}
@@ -498,10 +513,10 @@
     };
 
     @DataClass.Generated(
-            time = 1634716126923L,
+            time = 1635190605212L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
-            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate  long mCreationTime\nprivate  boolean mSkipPrompt\nprivate  void onConstructed()\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic  void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
+            inputSignatures = "private static final  java.lang.String LOG_TAG\npublic static final  java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate  boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate  long mCreationTime\nprivate  boolean mSkipPrompt\nprivate  void onConstructed()\npublic  void setCallingPackage(java.lang.String)\npublic  void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic  void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate  boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 665f626..e6df79e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3000,12 +3000,17 @@
      *
      * @param receiver The BroadcastReceiver to handle the broadcast.
      * @param filter Selects the Intent broadcasts to be received.
-     * @param flags Additional options for the receiver. As of
-     * Android T, either {@link #RECEIVER_EXPORTED} or
+     * @param flags Additional options for the receiver. For apps targeting
+     * {@link android.os.Build.VERSION_CODES#TIRAMISU},
+     *              either {@link #RECEIVER_EXPORTED} or
      * {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
-     *            for protected broadcasts, and may additionally specify
-     *            {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS} if {@link #RECEIVER_EXPORTED} is
-     *            specified.
+     *              for protected broadcasts or an exception will be thrown. If
+     *              {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
+     *              specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
+     *              protected broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
+     *              Android SDK. If both {@link #RECEIVER_EXPORTED} and
+     *              {@link #RECEIVER_NOT_EXPORTED} are specified, an exception will be thrown as
+     *              well.
      *
      * @return The first sticky intent found that matches <var>filter</var>,
      *         or null if there are none.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e838d93..14e43b3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1148,6 +1148,10 @@
      * numbers.  Applications can <strong>dial</strong> emergency numbers using
      * {@link #ACTION_DIAL}, however.
      *
+     * <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use
+     * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than
+     * relying on this intent.
+     *
      * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M}
      * and above and declares as using the {@link android.Manifest.permission#CALL_PHONE}
      * permission which is not granted, then attempting to use this action will
@@ -1857,14 +1861,19 @@
             "android.intent.action.MANAGE_APP_PERMISSIONS";
 
     /**
-     * Activity action: Launch UI to manage a specific permissions of an app.
+     * Activity action: Launch UI to manage a specific permission group of an app.
      * <p>
      * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
      * will be managed by the launched UI.
      * </p>
      * <p>
      * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
-     * that should be managed by the launched UI.
+     * whose group should be managed by the launched UI.
+     * </p>
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group
+     * that should be managed by the launched UI. Do not send both this and EXTRA_PERMISSION_NAME
+     * together.
      * </p>
      * <p>
      * <li> {@link #EXTRA_USER} specifies the {@link UserHandle} of the user that owns the app.
@@ -1875,6 +1884,7 @@
      *
      * @see #EXTRA_PACKAGE_NAME
      * @see #EXTRA_PERMISSION_NAME
+     * @see #EXTRA_PERMISSION_GROUP_NAME
      * @see #EXTRA_USER
      *
      * @hide
@@ -2163,14 +2173,15 @@
     /**
      * Activity action: Launch UI to manage which apps have a given permission.
      * <p>
-     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission group
-     * which will be managed by the launched UI.
+     * Input: {@link #EXTRA_PERMISSION_NAME} or {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the
+     * permission group which will be managed by the launched UI.
      * </p>
      * <p>
      * Output: Nothing.
      * </p>
      *
      * @see #EXTRA_PERMISSION_NAME
+     * @see #EXTRA_PERMISSION_GROUP_NAME
      *
      * @hide
      */
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e7ca76e..58f20ef 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -448,6 +448,12 @@
      * @see android.app.Activity#setVrModeEnabled(boolean, ComponentName)
      */
     public static final int FLAG_ENABLE_VR_MODE = 0x8000;
+    /**
+     * Bit in {@link #flags} indicating if the activity can be displayed on a remote device.
+     * Corresponds to {@link android.R.attr#canDisplayOnRemoteDevices}
+     * @hide
+     */
+    public static final int FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES = 0x10000;
 
     /**
      * Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is
@@ -996,10 +1002,9 @@
      * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
      * OVERRIDE_MIN_ASPECT_RATIO_LARGE
      *
-     * If OVERRIDE_MIN_ASPECT_RATIO is applied, and the activity's orientation is fixed to
-     * portrait, the min aspect ratio given in the app's manifest will be overridden to the
-     * largest enabled aspect ratio treatment unless the app's manifest value is higher.
-     * TODO(b/203647190): add OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY instead of portrait by default
+     * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's manifest
+     * will be overridden to the largest enabled aspect ratio treatment unless the app's manifest
+     * value is higher.
      * @hide
      */
     @ChangeId
@@ -1009,6 +1014,19 @@
     public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id
 
     /**
+     * This change id restricts treatments that force a given min aspect ratio to activities
+     * whose orientation is fixed to portrait.
+     *
+     * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+     * @hide
+     */
+    @ChangeId
+    @Overridable
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S_V2)
+    @TestApi
+    public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
+
+    /**
      * This change id sets the activity's min aspect ratio to a medium value as defined by
      * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
      *
@@ -1337,9 +1355,7 @@
      */
     @SizeChangesSupportMode
     public int supportsSizeChanges() {
-        if (CompatChanges.isChangeEnabled(FORCE_NON_RESIZE_APP,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+        if (isChangeEnabled(FORCE_NON_RESIZE_APP)) {
             return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
         }
 
@@ -1347,9 +1363,7 @@
             return SIZE_CHANGES_SUPPORTED_METADATA;
         }
 
-        if (CompatChanges.isChangeEnabled(FORCE_RESIZE_APP,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+        if (isChangeEnabled(FORCE_RESIZE_APP)) {
             return SIZE_CHANGES_SUPPORTED_OVERRIDE;
         }
 
@@ -1361,9 +1375,7 @@
      * @hide
      */
     public boolean neverSandboxDisplayApis() {
-        return CompatChanges.isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))
+        return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
                 || ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
     }
 
@@ -1372,9 +1384,7 @@
      * @hide
      */
     public boolean alwaysSandboxDisplayApis() {
-        return CompatChanges.isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))
+        return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
                 || ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
     }
 
@@ -1404,31 +1414,28 @@
      * @hide
      */
     public float getMinAspectRatio(@ScreenOrientation int orientation) {
-        // TODO(b/203647190): check orientation only if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY
-        // In case the activity's orientation isn't fixed to portrait, OVERRIDE_MIN_ASPECT_RATIO
-        // shouldn't be applied.
-        if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))
-                || !isFixedOrientationPortrait(orientation)) {
+        if (applicationInfo == null || !isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || (
+                isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+                        && !isFixedOrientationPortrait(orientation))) {
             return mMinAspectRatio;
         }
 
-        if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+        if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
             return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio);
         }
 
-        if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+        if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
             return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio);
         }
 
         return mMinAspectRatio;
     }
 
+    private boolean isChangeEnabled(long changeId) {
+        return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
+                UserHandle.getUserHandleForUid(applicationInfo.uid));
+    }
+
     /** @hide */
     public float getManifestMinAspectRatio() {
         return mMinAspectRatio;
@@ -1496,9 +1503,7 @@
      * @hide
      */
     public boolean shouldCheckMinWidthHeightForMultiWindow() {
-        return CompatChanges.isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW,
-                applicationInfo.packageName,
-                UserHandle.getUserHandleForUid(applicationInfo.uid));
+        return isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW);
     }
 
     public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0a10aaa..e3a5de5 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -194,6 +194,8 @@
 
     public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
     public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
+    public static final String METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES =
+            "android.can_display_on_remote_devices";
     public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
             "android.activity_window_layout_affinity";
     public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 45241b0..2ddf923 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -420,6 +420,21 @@
             }
         }
 
+        if (!isAlias) {
+            // Default allow the activity to be displayed on a remote device unless it explicitly
+            // set to false.
+            boolean canDisplayOnRemoteDevices = array.getBoolean(
+                    R.styleable.AndroidManifestActivity_canDisplayOnRemoteDevices, true);
+            if (activity.getMetaData() != null && !activity.getMetaData().getBoolean(
+                    ParsingPackageUtils.METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES, true)) {
+                canDisplayOnRemoteDevices = false;
+            }
+            if (canDisplayOnRemoteDevices) {
+                activity.setFlags(activity.getFlags()
+                        | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES);
+            }
+        }
+
         ParseResult<ActivityInfo.WindowLayout> layoutResult =
                 resolveActivityWindowLayout(activity, input);
         if (layoutResult.isError()) {
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c8c122d..6b5bec9 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -408,6 +408,19 @@
         }
 
         /**
+         * Flag to decide if authentication should ignore enrollment state.
+         * Defaults to false (not ignoring enrollment state)
+         * @param ignoreEnrollmentState
+         * @return This builder.
+         * @hide
+         */
+        @NonNull
+        public Builder setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+            mPromptInfo.setIgnoreEnrollmentState(ignoreEnrollmentState);
+            return this;
+        }
+
+        /**
          * Creates a {@link BiometricPrompt}.
          *
          * @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 339c654..e6b762a 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -45,6 +45,7 @@
     private boolean mReceiveSystemEvents;
     @NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>();
     private boolean mAllowBackgroundAuthentication;
+    private boolean mIgnoreEnrollmentState;
 
     public PromptInfo() {
 
@@ -66,6 +67,7 @@
         mReceiveSystemEvents = in.readBoolean();
         mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
         mAllowBackgroundAuthentication = in.readBoolean();
+        mIgnoreEnrollmentState = in.readBoolean();
     }
 
     public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -102,6 +104,7 @@
         dest.writeBoolean(mReceiveSystemEvents);
         dest.writeList(mAllowedSensorIds);
         dest.writeBoolean(mAllowBackgroundAuthentication);
+        dest.writeBoolean(mIgnoreEnrollmentState);
     }
 
     public boolean containsTestConfigurations() {
@@ -192,6 +195,10 @@
         mAllowBackgroundAuthentication = allow;
     }
 
+    public void setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+        mIgnoreEnrollmentState = ignoreEnrollmentState;
+    }
+
     // Getters
 
     public CharSequence getTitle() {
@@ -261,4 +268,8 @@
     public boolean isAllowBackgroundAuthentication() {
         return mAllowBackgroundAuthentication;
     }
+
+    public boolean isIgnoreEnrollmentState() {
+        return mIgnoreEnrollmentState;
+    }
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a3d595c..fe04e5d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -531,7 +531,7 @@
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
-        authenticate(crypto, cancel, callback, handler, mContext.getUserId());
+        authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, mContext.getUserId(), flags);
     }
 
     /**
@@ -541,7 +541,7 @@
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             @NonNull AuthenticationCallback callback, Handler handler, int userId) {
-        authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId);
+        authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId, 0 /* flags */);
     }
 
     /**
@@ -550,7 +550,8 @@
      */
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+            @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId,
+            int flags) {
 
         FrameworkStatsLog.write(FrameworkStatsLog.AUTH_DEPRECATED_API_USED,
                 AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE,
@@ -566,6 +567,8 @@
             return;
         }
 
+        final boolean ignoreEnrollmentState = flags == 0 ? false : true;
+
         if (mService != null) {
             try {
                 useHandler(handler);
@@ -573,7 +576,7 @@
                 mCryptoObject = crypto;
                 final long operationId = crypto != null ? crypto.getOpId() : 0;
                 final long authId = mService.authenticate(mToken, operationId, sensorId, userId,
-                        mServiceReceiver, mContext.getOpPackageName());
+                        mServiceReceiver, mContext.getOpPackageName(), ignoreEnrollmentState);
                 if (cancel != null) {
                     cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
                 }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index de94b2f..ba1dc6d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -52,7 +52,8 @@
     // permission. This is effectively deprecated, since it only comes through FingerprintManager
     // now. A requestId is returned that can be used to cancel this operation.
     long authenticate(IBinder token, long operationId, int sensorId, int userId,
-            IFingerprintServiceReceiver receiver, String opPackageName);
+            IFingerprintServiceReceiver receiver, String opPackageName,
+            boolean shouldIgnoreEnrollmentState);
 
     // Uses the fingerprint hardware to detect for the presence of a finger, without giving details
     // about accept/reject/lockout. A requestId is returned that can be used to cancel this
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index ae97fe7..ed617af 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -31,6 +31,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
 
 import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.inputmethod.CompletableFutureUtil;
@@ -272,6 +273,17 @@
     }
 
     @AnyThread
+    public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+            @Nullable TextAttribute textAttribute) {
+        final boolean handled =
+                mInvoker.commitText(text, newCursorPosition, textAttribute);
+        if (handled) {
+            notifyUserActionIfNecessary();
+        }
+        return handled;
+    }
+
+    @AnyThread
     private void notifyUserActionIfNecessary() {
         final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
         if (imsInternal == null) {
@@ -311,6 +323,11 @@
     }
 
     @AnyThread
+    public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+        return mInvoker.setComposingRegion(start, end, textAttribute);
+    }
+
+    @AnyThread
     public boolean setComposingText(CharSequence text, int newCursorPosition) {
         final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
         if (handled) {
@@ -320,6 +337,16 @@
     }
 
     @AnyThread
+    public boolean setComposingText(CharSequence text, int newCursorPosition,
+            @Nullable TextAttribute textAttribute) {
+        final boolean handled = mInvoker.setComposingText(text, newCursorPosition, textAttribute);
+        if (handled) {
+            notifyUserActionIfNecessary();
+        }
+        return handled;
+    }
+
+    @AnyThread
     public boolean finishComposingText() {
         return mInvoker.finishComposingText();
     }
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 7ef5bac..8605248 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -232,11 +232,10 @@
         ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
         ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
 
-        // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
-        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
-        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
+        ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
     }
 
     private static final Set<String> ENABLED_ALGOS =
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 5a2f27d..09e5a8f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2992,8 +2992,24 @@
      * from the parcel at the current dataPosition().
      */
     public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
-        int N = readInt();
-        readMapInternal(outVal, N, loader);
+        int n = readInt();
+        readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
+    }
+
+    /**
+     * Same as {@link #readMap(Map, ClassLoader)} but accepts {@code clazzKey} and
+     * {@code clazzValue} parameter as the types required for each key and value pair.
+     *
+     * @throws BadParcelableException If the item to be deserialized is not an instance of that
+     * class or any of its children class
+     */
+    public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
+            @Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
+            @NonNull Class<V> clazzValue) {
+        Objects.requireNonNull(clazzKey);
+        Objects.requireNonNull(clazzValue);
+        int n = readInt();
+        readMapInternal(outVal, n, loader, clazzKey, clazzValue);
     }
 
     /**
@@ -3031,16 +3047,38 @@
     @Nullable
     public final HashMap readHashMap(@Nullable ClassLoader loader)
     {
-        int N = readInt();
-        if (N < 0) {
+        int n = readInt();
+        if (n < 0) {
             return null;
         }
-        HashMap m = new HashMap(N);
-        readMapInternal(m, N, loader);
+        HashMap m = new HashMap(n);
+        readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
         return m;
     }
 
     /**
+     * Same as {@link #readHashMap(ClassLoader)} but accepts {@code clazzKey} and
+     * {@code clazzValue} parameter as the types required for each key and value pair.
+     *
+     * @throws BadParcelableException if the item to be deserialized is not an instance of that
+     * class or any of its children class
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    @Nullable
+    public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
+            @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+        Objects.requireNonNull(clazzKey);
+        Objects.requireNonNull(clazzValue);
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        HashMap<K, V> map = new HashMap<>(n);
+        readMapInternal(map, n, loader, clazzKey, clazzValue);
+        return map;
+    }
+
+    /**
      * Read and return a new Bundle object from the parcel at the current
      * dataPosition().  Returns null if the previously written Bundle object was
      * null.
@@ -4472,13 +4510,23 @@
         destroy();
     }
 
-    /* package */ void readMapInternal(@NonNull Map outVal, int N,
+    /**
+     * To be replaced by {@link #readMapInternal(Map, int, ClassLoader, Class, Class)}, but keep
+     * the old API for compatibility usages.
+     */
+    /* package */ void readMapInternal(@NonNull Map outVal, int n,
             @Nullable ClassLoader loader) {
-        while (N > 0) {
-            Object key = readValue(loader);
-            Object value = readValue(loader);
+        readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
+    }
+
+    /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
+            @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+            @Nullable Class<V> clazzValue) {
+        while (n > 0) {
+            K key = readValue(loader, clazzKey);
+            V value = readValue(loader, clazzValue);
             outVal.put(key, value);
-            N--;
+            n--;
         }
     }
 
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 4a6216e..3d5abb3 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -23,10 +23,15 @@
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Random;
 
 /**
  * Representation of a user on the device.
@@ -119,19 +124,46 @@
     public static final int MIN_SECONDARY_USER_ID = 10;
 
     /**
-     * Arbitrary user handle cache size. We use the cache even when {@link #MU_ENABLED} is false
-     * anyway, so we can always assume in CTS that UserHandle.of(10) returns a cached instance
-     * even on non-multiuser devices.
+     * (Arbitrary) user handle cache size.
+     * {@link #CACHED_USER_HANDLES} caches user handles in the range of
+     * [{@link #MIN_SECONDARY_USER_ID}, {@link #MIN_SECONDARY_USER_ID} + {@link #NUM_CACHED_USERS}).
+     *
+     * For other users, we cache UserHandles in {link #sExtraUserHandleCache}.
+     *
+     * Normally, {@link #CACHED_USER_HANDLES} should cover all existing users, but use
+     * {link #sExtraUserHandleCache} to ensure {@link UserHandle#of} will not cause too many
+     * object allocations even if the device happens to have a secondary user with a large number
+     * (e.g. the user kept creating and removing the guest user?).
      */
-    private static final int NUM_CACHED_USERS = 4;
+    private static final int NUM_CACHED_USERS = MU_ENABLED ? 8 : 0;
 
-    private static final UserHandle[] CACHED_USER_INFOS = new UserHandle[NUM_CACHED_USERS];
+    /** @see #NUM_CACHED_USERS} */
+    private static final UserHandle[] CACHED_USER_HANDLES = new UserHandle[NUM_CACHED_USERS];
+
+    /**
+     * Extra cache for users beyond CACHED_USER_HANDLES.
+     *
+     * @see #NUM_CACHED_USERS
+     * @hide
+     */
+    @GuardedBy("sExtraUserHandleCache")
+    @VisibleForTesting
+    public static final SparseArray<UserHandle> sExtraUserHandleCache = new SparseArray<>(0);
+
+    /**
+     * Max size of {@link #sExtraUserHandleCache}. Once it reaches this size, we select
+     * an element to remove at random.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static final int MAX_EXTRA_USER_HANDLE_CACHE_SIZE = 32;
 
     static {
         // Not lazily initializing the cache, so that we can share them across processes.
         // (We'll create them in zygote.)
-        for (int i = 0; i < CACHED_USER_INFOS.length; i++) {
-            CACHED_USER_INFOS[i] = new UserHandle(MIN_SECONDARY_USER_ID + i);
+        for (int i = 0; i < CACHED_USER_HANDLES.length; i++) {
+            CACHED_USER_HANDLES[i] = new UserHandle(MIN_SECONDARY_USER_ID + i);
         }
     }
 
@@ -302,13 +334,31 @@
                 return CURRENT_OR_SELF;
         }
         if (userId >= MIN_SECONDARY_USER_ID
-                && userId < (MIN_SECONDARY_USER_ID + CACHED_USER_INFOS.length)) {
-            return CACHED_USER_INFOS[userId - MIN_SECONDARY_USER_ID];
+                && userId < (MIN_SECONDARY_USER_ID + CACHED_USER_HANDLES.length)) {
+            return CACHED_USER_HANDLES[userId - MIN_SECONDARY_USER_ID];
         }
         if (userId == USER_NULL) { // Not common.
             return NULL;
         }
-        return new UserHandle(userId);
+        return getUserHandleFromExtraCache(userId);
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public static UserHandle getUserHandleFromExtraCache(@UserIdInt int userId) {
+        synchronized (sExtraUserHandleCache) {
+            final UserHandle extraCached = sExtraUserHandleCache.get(userId);
+            if (extraCached != null) {
+                return extraCached;
+            }
+            if (sExtraUserHandleCache.size() >= MAX_EXTRA_USER_HANDLE_CACHE_SIZE) {
+                sExtraUserHandleCache.removeAt(
+                        (new Random()).nextInt(MAX_EXTRA_USER_HANDLE_CACHE_SIZE));
+            }
+            final UserHandle newHandle = new UserHandle(userId);
+            sExtraUserHandleCache.put(userId, newHandle);
+            return newHandle;
+        }
     }
 
     /**
diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java
index 74ce515..6c648f1 100644
--- a/core/java/android/os/health/HealthStats.java
+++ b/core/java/android/os/health/HealthStats.java
@@ -32,7 +32,7 @@
  * Each of the keys references data in one of five data types:
  *
  * <p>
- * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may
+ * A <b>measurement</b> metric contains a single {@code long} value. That value may
  * be a count, a time, or some other type of value. The unit for a measurement
  * (COUNT, MS, etc) will always be in the name of the constant for the key to
  * retrieve it. For example, the
diff --git a/core/java/android/os/health/UidHealthStats.java b/core/java/android/os/health/UidHealthStats.java
index afc9d78..488a542 100644
--- a/core/java/android/os/health/UidHealthStats.java
+++ b/core/java/android/os/health/UidHealthStats.java
@@ -43,14 +43,14 @@
 
     /**
      * How many milliseconds this statistics report covers in wall-clock time while the
-     * device was on battery including both screen-on and screen-off time.
+     * device was on battery including only screen-off time.
      */
     @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
     public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 3;
 
     /**
      * How many milliseconds this statistics report covers that the CPU was running while the
-     * device was on battery including both screen-on and screen-off time.
+     * device was on battery including only screen-off time.
      */
     @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
     public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 4;
@@ -65,7 +65,7 @@
 
     /**
      * Key for a TimerStat for the times a
-     * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK full wake lock}
+     * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock}
      * was acquired for this uid.
      */
     @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index dd31e02..21c1feb 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -648,6 +648,14 @@
     public static final String NAMESPACE_GAME_OVERLAY = "game_overlay";
 
     /**
+     * Namespace for Android Virtualization Framework related features accessible by native code.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE =
+            "virtualization_framework_native";
+
+    /**
      * Namespace for Constrain Display APIs related features.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 295ed77..c54b56d8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7158,6 +7158,13 @@
         public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
 
         /**
+         * Whether select sound track with audio description by default.
+         * @hide
+         */
+        public static final String ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT =
+                "enabled_accessibility_audio_description_by_default";
+
+        /**
          * Setting specifying if the accessibility shortcut is enabled.
          * @hide
          */
@@ -10415,14 +10422,6 @@
                 "enable_accessibility_global_gesture_enabled";
 
         /**
-         * Whether select sound track with audio description by default.
-         * @hide
-         */
-        @Readable
-        public static final String ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT =
-                "enable_accessibility_audio_description_by_default";
-
-        /**
          * Whether Airplane Mode is on.
          */
         @Readable
@@ -16609,12 +16608,6 @@
             public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number";
 
             /**
-             * The different levels of screen brightness the user can select.
-             * @hide
-             */
-            public static final String SCREEN_BRIGHTNESS_LEVEL = "screen_brightness_level";
-
-            /**
              * The mobile signal detector setting.
              * @hide
              */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index aa1bbc5..1b38f59 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5374,5 +5374,21 @@
         */
         public static final String COLUMN_NR_ADVANCED_CALLING_ENABLED =
                 "nr_advanced_calling_enabled";
+
+        /**
+         * TelephonyProvider column name for the phone number from source CARRIER
+         *
+         * @hide
+         */
+        public static final String COLUMN_PHONE_NUMBER_SOURCE_CARRIER =
+                "phone_number_source_carrier";
+
+        /**
+         * TelephonyProvider column name for the phone number from source IMS
+         *
+         * @hide
+         */
+        public static final String COLUMN_PHONE_NUMBER_SOURCE_IMS =
+                "phone_number_source_ims";
     }
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2d263a5..c77399f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1575,7 +1575,7 @@
         void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
                 float xOffsetStep) {
             // to save creating a runnable, check twice
-            long current = SystemClock.elapsedRealtime();
+            long current = System.currentTimeMillis();
             long lapsed = current - currentPage.getLastUpdateTime();
             // Always update the page when the last update time is <= 0
             // This is important especially when the device first boots
@@ -1768,6 +1768,7 @@
                     return;
                 }
             }
+            processLocalColors(mPendingXOffset, mPendingYOffset);
         }
 
         /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c88d4f8..ce96eca 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -211,13 +211,13 @@
 import java.io.StringWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.function.Consumer;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
 
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -730,7 +730,7 @@
 
     /**
      * This is only used on the RenderThread when handling a blast sync. Specifically, it's only
-     * used when calling {@link BLASTBufferQueue#setNextTransaction(Transaction)} and then merged
+     * used when calling {@link BLASTBufferQueue#setSyncTransaction(Transaction)} and then merged
      * with a tmp transaction on the Render Thread. The tmp transaction is then merged into
      * {@link #mSurfaceChangedTransaction} on the UI Thread, avoiding any threading issues.
      */
@@ -3999,7 +3999,7 @@
             // draw attempt. The next transaction and transaction complete callback were only set
             // for the current draw attempt.
             if (frameWasNotDrawn) {
-                mBlastBufferQueue.setNextTransaction(null);
+                mBlastBufferQueue.setSyncTransaction(null);
                 // Apply the transactions that were sent to mergeWithNextTransaction since the
                 // frame didn't draw on this vsync. It's possible the frame will draw later, but
                 // it's better to not be sync than to block on a frame that may never come.
@@ -4095,7 +4095,7 @@
 
                 // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
                 // being modified and only sent to BlastBufferQueue.
-                mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+                mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction);
             }
         };
         registerRtFrameCallback(frameDrawingCallback);
diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
index bc9fb0a..72518db 100644
--- a/core/java/android/view/accessibility/MagnificationAnimationCallback.java
+++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
@@ -21,6 +21,9 @@
  * @hide
  */
 public interface MagnificationAnimationCallback {
+    MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
+    };
+
     /**
      * Called when the animation is finished or interrupted during animating.
      *
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 4a52b1f..35e60b8 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -528,10 +528,6 @@
      * If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
      * matter what user ID the calling process has.
      *
-     * <p>Note: This field will be silently ignored when
-     * {@link com.android.server.inputmethod.InputMethodSystemProperty#MULTI_CLIENT_IME_ENABLED} is
-     * {@code true}.</p>
-     *
      * <p>Note also that pseudo handles such as {@link UserHandle#ALL} are not supported.</p>
      *
      * @hide
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index c3d7836..3b15db2 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -545,6 +545,33 @@
     boolean setComposingText(CharSequence text, int newCursorPosition);
 
     /**
+     * The variant of {@link #setComposingText(CharSequence, int)}. This method is
+     * used to allow the IME to provide extra information while setting up composing text.
+     *
+     * @param text The composing text with styles if necessary. If no style
+     *        object attached to the text, the default style for composing text
+     *        is used. See {@link android.text.Spanned} for how to attach style
+     *        object to the text. {@link android.text.SpannableString} and
+     *        {@link android.text.SpannableStringBuilder} are two
+     *        implementations of the interface {@link android.text.Spanned}.
+     * @param newCursorPosition The new cursor position around the text. If
+     *        > 0, this is relative to the end of the text - 1; if <= 0, this
+     *        is relative to the start of the text. So a value of 1 will
+     *        always advance you to the position after the full text being
+     *        inserted. Note that this means you can't position the cursor
+     *        within the text, because the editor can make modifications to
+     *        the text you are providing so it is not possible to correctly
+     *        specify locations there.
+     * @param textAttribute The extra information about the text.
+     * @return true on success, false if the input connection is no longer
+     *
+     */
+    default boolean setComposingText(@NonNull CharSequence text, int newCursorPosition,
+            @Nullable TextAttribute textAttribute) {
+        return setComposingText(text, newCursorPosition);
+    }
+
+    /**
      * Mark a certain region of text as composing text. If there was a
      * composing region, the characters are left as they were and the
      * composing span removed, as if {@link #finishComposingText()}
@@ -579,6 +606,22 @@
     boolean setComposingRegion(int start, int end);
 
     /**
+     * The variant of {@link InputConnection#setComposingRegion(int, int)}. This method is
+     * used to allow the IME to provide extra information while setting up text.
+     *
+     * @param start the position in the text at which the composing region begins
+     * @param end the position in the text at which the composing region ends
+     * @param textAttribute The extra information about the text.
+     * @return {@code true} on success, {@code false} if the input connection is no longer valid.
+     *         Since Android {@link android.os.Build.VERSION_CODES#N} until
+     *         {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
+     *         the target application does not implement this method.
+     */
+    default boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+        return setComposingRegion(start, end);
+    }
+
+    /**
      * Have the text editor finish whatever composing text is
      * currently active. This simply leaves the text as-is, removing
      * any special composing styling or other state that was around
@@ -634,6 +677,28 @@
     boolean commitText(CharSequence text, int newCursorPosition);
 
     /**
+     * The variant of {@link InputConnection#commitText(CharSequence, int)}. This method is
+     * used to allow the IME to provide extra information while setting up text.
+     *
+     * @param text The text to commit. This may include styles.
+     * @param newCursorPosition The new cursor position around the text,
+     *        in Java characters. If > 0, this is relative to the end
+     *        of the text - 1; if <= 0, this is relative to the start
+     *        of the text. So a value of 1 will always advance the cursor
+     *        to the position after the full text being inserted. Note that
+     *        this means you can't position the cursor within the text,
+     *        because the editor can make modifications to the text
+     *        you are providing so it is not possible to correctly specify
+     *        locations there.
+     * @param textAttribute The extra information about the text.
+     * @return true on success, false if the input connection is no longer
+     */
+    default boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+            @Nullable TextAttribute textAttribute) {
+        return commitText(text, newCursorPosition);
+    }
+
+    /**
      * Commit a completion the user has selected from the possible ones
      * previously reported to {@link InputMethodSession#displayCompletions
      * InputMethodSession#displayCompletions(CompletionInfo[])} or
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index a99e9b8..7a88a75 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -17,6 +17,7 @@
 package android.view.inputmethod;
 
 import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.Handler;
@@ -158,6 +159,16 @@
      * @throws NullPointerException if the target is {@code null}.
      */
     @Override
+    public boolean setComposingText(@NonNull CharSequence text,
+            int newCursorPosition, @Nullable TextAttribute textAttribute) {
+        return mTarget.setComposingText(text, newCursorPosition, textAttribute);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws NullPointerException if the target is {@code null}.
+     */
+    @Override
     public boolean setComposingRegion(int start, int end) {
         return mTarget.setComposingRegion(start, end);
     }
@@ -167,6 +178,15 @@
      * @throws NullPointerException if the target is {@code null}.
      */
     @Override
+    public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+        return mTarget.setComposingRegion(start, end, textAttribute);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws NullPointerException if the target is {@code null}.
+     */
+    @Override
     public boolean finishComposingText() {
         return mTarget.finishComposingText();
     }
@@ -185,6 +205,16 @@
      * @throws NullPointerException if the target is {@code null}.
      */
     @Override
+    public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+            @Nullable TextAttribute textAttribute) {
+        return mTarget.commitText(text, newCursorPosition, textAttribute);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws NullPointerException if the target is {@code null}.
+     */
+    @Override
     public boolean commitCompletion(CompletionInfo text) {
         return mTarget.commitCompletion(text);
     }
diff --git a/core/java/android/view/inputmethod/TextAttribute.aidl b/core/java/android/view/inputmethod/TextAttribute.aidl
new file mode 100644
index 0000000..5f296d9
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextAttribute.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 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.inputmethod;
+
+parcelable TextAttribute;
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/TextAttribute.java b/core/java/android/view/inputmethod/TextAttribute.java
new file mode 100644
index 0000000..bc76e78
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextAttribute.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The data class that IME can take extra information to applications when setting the text.
+ *
+ * See {@link InputConnection#commitText(CharSequence, int, TextAttribute)} and
+ * {@link InputConnection#setComposingRegion(int, int, TextAttribute)} and
+ * {@link InputConnection#setComposingText(CharSequence, int, TextAttribute)}
+ */
+public final class TextAttribute implements Parcelable {
+    private final @NonNull List<String> mTextConversionSuggestions;
+    private final @NonNull PersistableBundle mExtras;
+
+    private TextAttribute(TextAttributeBuilder builder) {
+        mTextConversionSuggestions = builder.mTextConversionSuggestions;
+        mExtras = builder.mExtras;
+    }
+
+    private TextAttribute(Parcel source) {
+        mTextConversionSuggestions = source.createStringArrayList();
+        mExtras = source.readPersistableBundle();
+    }
+
+    /**
+     * Get the list of text conversion suggestions. More text conversion details in
+     * {@link TextAttributeBuilder#setTextConversionSuggestions(List)}.
+     *
+     * @return List of text conversion suggestions. If the list is empty, it means that IME not set
+     * this field or IME didn't have suggestions for applications.
+     */
+    public @NonNull List<String> getTextConversionSuggestions() {
+        return mTextConversionSuggestions;
+    }
+
+    /**
+     * Get the extras data. More extras data details in
+     * {@link TextAttributeBuilder#setExtras(PersistableBundle)}.
+     *
+     * @return Extras data. If the Bundle is empty, it means that IME not set this field or IME
+     * didn't have extras data.
+     */
+    public @NonNull PersistableBundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Builder for creating a {@link TextAttribute}.
+     */
+    public static final class TextAttributeBuilder {
+        private List<String> mTextConversionSuggestions = new ArrayList<>();
+        private PersistableBundle mExtras = new PersistableBundle();
+
+        /**
+         * Sets text conversion suggestions.
+         *
+         * <p>Text conversion suggestion is for some transliteration languages which has
+         * pronunciation characters and target characters. When the user is typing the pronunciation
+         * characters, the input method can insert possible target characters into this list so that
+         * the editor authors can provide suggestion before the user enters the complete
+         * pronunciation characters.</p>
+         *
+         * @param textConversionSuggestions The list of text conversion suggestions.
+         * @return This builder
+         */
+        public @NonNull TextAttributeBuilder setTextConversionSuggestions(
+                @NonNull List<String> textConversionSuggestions) {
+            mTextConversionSuggestions = Collections.unmodifiableList(textConversionSuggestions);
+            return this;
+        }
+
+        /**
+         * Sets extras data.
+         *
+         * <p>Any extra data to supply to the applications. This field is for extended communication
+         * with IME if there is data not defined in framework.</p>
+         *
+         * @return This builder.
+         */
+        public @NonNull TextAttributeBuilder setExtras(@NonNull PersistableBundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * @return a new {@link TextAttribute}.
+         */
+        public @NonNull TextAttribute build() {
+            return new TextAttribute(this);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStringList(mTextConversionSuggestions);
+        dest.writePersistableBundle(mExtras);
+    }
+
+    public static final @NonNull Parcelable.Creator<TextAttribute> CREATOR =
+            new Parcelable.Creator<TextAttribute>() {
+        @Override
+        public TextAttribute createFromParcel(Parcel source) {
+            return new TextAttribute(source);
+        }
+
+        @Override
+        public TextAttribute[] newArray(int size) {
+            return new TextAttribute[size];
+        }
+    };
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2357d13..f724285 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5735,24 +5735,44 @@
         return previousLayoutId == getLayoutId() && mViewId == overrideId;
     }
 
-    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
-    // should set it to false.
-    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
-            ColorResources colorResources, boolean topLevel) {
-
+    /**
+     * Returns the RemoteViews that should be used in the reapply operation.
+     *
+     * If the current RemoteViews has multiple layout, this will select the correct one.
+     *
+     * @throws RuntimeException If the current RemoteViews should not be reapplied onto the provided
+     * View.
+     */
+    private RemoteViews getRemoteViewsToReapply(Context context, View v, @Nullable SizeF size) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         // In the case that a view has this RemoteViews applied in one orientation or size, is
         // persisted across change, and has the RemoteViews re-applied in a different situation
         // (orientation or size), we throw an exception, since the layouts may be completely
         // unrelated.
-        if (hasMultipleLayouts()) {
+        // If the ViewID has been changed on the view, or is changed by the RemoteViews, we also
+        // may throw an exception, as the RemoteViews will probably not apply properly.
+        // However, we need to let potentially unrelated RemoteViews apply, as this lack of testing
+        // is already used in production code in some apps.
+        if (hasMultipleLayouts()
+                || rvToApply.mViewId != View.NO_ID
+                || v.getTag(R.id.remote_views_override_id) != null) {
             if (!rvToApply.canRecycleView(v)) {
                 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
                         " that does not share the same root layout id.");
             }
         }
 
+        return rvToApply;
+    }
+
+    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+    // should set it to false.
+    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
+            ColorResources colorResources, boolean topLevel) {
+
+        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+
         rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
 
         // If the parent of the view is has is a root, resolve the recycling.
@@ -5789,17 +5809,7 @@
     public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
-        RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
-        // In the case that a view has this RemoteViews applied in one orientation, is persisted
-        // across orientation change, and has the RemoteViews re-applied in the new orientation,
-        // we throw an exception, since the layouts may be completely unrelated.
-        if (hasMultipleLayouts()) {
-            if (!rvToApply.canRecycleView(v)) {
-                throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
-                        " that does not share the same root layout id.");
-            }
-        }
+        RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
 
         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
                 context, listener, handler, colorResources, v, true /* topLevel */)
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
index efdf483..4dbd941 100644
--- a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -18,6 +18,7 @@
 
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.view.KeyEvent;
@@ -27,6 +28,7 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
 
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.view.IInputContext;
@@ -211,6 +213,28 @@
     }
 
     /**
+     * Invokes {@link IInputContext#commitTextWithTextAttribute(InputConnectionCommandHeader, int,
+     * CharSequence)}.
+     *
+     * @param text {@code text} parameter to be passed.
+     * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+     * @param textAttribute The extra information about the text.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean commitText(CharSequence text, int newCursorPosition,
+            @Nullable TextAttribute textAttribute) {
+        try {
+            mIInputContext.commitTextWithTextAttribute(
+                    createHeader(), text, newCursorPosition, textAttribute);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Invokes {@link IInputContext#commitCompletion(InputConnectionCommandHeader, CompletionInfo)}.
      *
      * @param text {@code text} parameter to be passed.
@@ -315,6 +339,27 @@
     }
 
     /**
+     * Invokes {@link IInputContext#setComposingRegionWithTextAttribute(
+     * InputConnectionCommandHeader, int, int, TextAttribute)}.
+     *
+     * @param start {@code id} parameter to be passed.
+     * @param end {@code id} parameter to be passed.
+     * @param textAttribute The extra information about the text.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+        try {
+            mIInputContext.setComposingRegionWithTextAttribute(
+                    createHeader(), start, end, textAttribute);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Invokes
      * {@link IInputContext#setComposingText(InputConnectionCommandHeader, CharSequence, int)}.
      *
@@ -334,6 +379,28 @@
     }
 
     /**
+     * Invokes {@link IInputContext#setComposingTextWithTextAttribute(InputConnectionCommandHeader,
+     * CharSequence, int, TextAttribute)}.
+     *
+     * @param text {@code text} parameter to be passed.
+     * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+     * @param textAttribute The extra information about the text.
+     * @return {@code true} if the invocation is completed without {@link RemoteException}.
+     *         {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean setComposingText(CharSequence text, int newCursorPosition,
+            @Nullable TextAttribute textAttribute) {
+        try {
+            mIInputContext.setComposingTextWithTextAttribute(
+                    createHeader(), text, newCursorPosition, textAttribute);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Invokes {@link IInputContext#finishComposingText(InputConnectionCommandHeader)}.
      *
      * @return {@code true} if the invocation is completed without {@link RemoteException}.
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 21358ab..5503226 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -42,6 +42,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.TextAttribute;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.infra.AndroidFuture;
@@ -385,6 +386,23 @@
 
     @Dispatching(cancellable = true)
     @Override
+    public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
+            int newCursorPosition, @Nullable TextAttribute textAttribute) {
+        dispatchWithTracing("commitTextWithTextAttribute", () -> {
+            if (header.mSessionId != mCurrentSessionId.get()) {
+                return;  // cancelled
+            }
+            InputConnection ic = getInputConnection();
+            if (ic == null || !isActive()) {
+                Log.w(TAG, "commitText on inactive InputConnection");
+                return;
+            }
+            ic.commitText(text, newCursorPosition, textAttribute);
+        });
+    }
+
+    @Dispatching(cancellable = true)
+    @Override
     public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
         dispatchWithTracing("commitCompletion", () -> {
             if (header.mSessionId != mCurrentSessionId.get()) {
@@ -489,6 +507,23 @@
 
     @Dispatching(cancellable = true)
     @Override
+    public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
+            int end, @Nullable TextAttribute textAttribute) {
+        dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
+            if (header.mSessionId != mCurrentSessionId.get()) {
+                return;  // cancelled
+            }
+            InputConnection ic = getInputConnection();
+            if (ic == null || !isActive()) {
+                Log.w(TAG, "setComposingRegion on inactive InputConnection");
+                return;
+            }
+            ic.setComposingRegion(start, end, textAttribute);
+        });
+    }
+
+    @Dispatching(cancellable = true)
+    @Override
     public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition) {
         dispatchWithTracing("setComposingText", () -> {
@@ -504,6 +539,23 @@
         });
     }
 
+    @Dispatching(cancellable = true)
+    @Override
+    public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
+            CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
+        dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
+            if (header.mSessionId != mCurrentSessionId.get()) {
+                return;  // cancelled
+            }
+            InputConnection ic = getInputConnection();
+            if (ic == null || !isActive()) {
+                Log.w(TAG, "setComposingText on inactive InputConnection");
+                return;
+            }
+            ic.setComposingText(text, newCursorPosition, textAttribute);
+        });
+    }
+
     /**
      * Dispatches {@link InputConnection#finishComposingText()}.
      *
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
index ac83987..dd4ff67 100644
--- a/core/java/com/android/internal/inputmethod/StartInputFlags.java
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -30,7 +30,9 @@
 @IntDef(flag = true, value = {
         StartInputFlags.VIEW_HAS_FOCUS,
         StartInputFlags.IS_TEXT_EDITOR,
-        StartInputFlags.INITIAL_CONNECTION})
+        StartInputFlags.INITIAL_CONNECTION,
+        StartInputFlags.WINDOW_GAINED_FOCUS,
+})
 public @interface StartInputFlags {
     /**
      * There is a focused view in the focused window.
@@ -40,17 +42,17 @@
     /**
      * The focused view is a text editor.
      */
-    int IS_TEXT_EDITOR = 2;
+    int IS_TEXT_EDITOR = 1 << 1;
 
     /**
      * An internal concept to distinguish "start" and "restart". This concept doesn't look well
      * documented hence we probably need to revisit this though.
      */
-    int INITIAL_CONNECTION = 4;
+    int INITIAL_CONNECTION = 1 << 2;
 
     /**
      * The start input happens when the window gained focus to call
      * {@code android.view.inputmethod.InputMethodManager#startInputAsyncOnWindowFocusGain}.
      */
-    int WINDOW_GAINED_FOCUS = 8;
+    int WINDOW_GAINED_FOCUS = 1 << 3;
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 4e758e6..f3cdf82 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -46,7 +46,7 @@
     void showWirelessChargingAnimation(int batteryLevel);
 
     void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher, boolean isMultiClientImeEnabled);
+            boolean showImeSwitcher);
     void setWindowState(int display, int window, int state);
 
     void showRecentApps(boolean triggeredFromAltTab);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 40a3951..1db7426 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -55,7 +55,7 @@
     @UnsupportedAppUsage
     void removeIcon(String slot);
     void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher, boolean isMultiClientImeEnabled);
+            boolean showImeSwitcher);
     void expandSettingsPanel(String subPanel);
 
     // ---- Methods below are for use by the status bar policy services ----
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index df55beb..7da0f11 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -22,6 +22,7 @@
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.TextAttribute;
 
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.InputConnectionCommandHeader;
@@ -52,10 +53,16 @@
     void setComposingText(in InputConnectionCommandHeader header, CharSequence text,
             int newCursorPosition);
 
+    void setComposingTextWithTextAttribute(in InputConnectionCommandHeader header,
+                CharSequence text, int newCursorPosition, in TextAttribute textAttribute);
+
     void finishComposingText(in InputConnectionCommandHeader header);
 
     void commitText(in InputConnectionCommandHeader header, CharSequence text,
-            int newCursorPosition);
+                int newCursorPosition);
+
+    void commitTextWithTextAttribute(in InputConnectionCommandHeader header, CharSequence text,
+            int newCursorPosition, in TextAttribute textAttribute);
 
     void commitCompletion(in InputConnectionCommandHeader header, in CompletionInfo completion);
 
@@ -82,6 +89,9 @@
 
     void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
 
+    void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
+            int end, in TextAttribute textAttribute);
+
     void getSelectedText(in InputConnectionCommandHeader header, int flags,
             in AndroidFuture future /* T=CharSequence */);
 
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a7362ab..cdfd089 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -61,10 +61,10 @@
                                                   queue->getSurface(includeSurfaceControlHandle));
 }
 
-static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
+static void nativeSetSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
-    queue->setNextTransaction(transaction);
+    queue->setSyncTransaction(transaction);
 }
 
 static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
@@ -98,7 +98,7 @@
         {"nativeCreate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreate},
         {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
-        {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
+        {"nativeSetSyncTransaction", "(JJ)V", (void*)nativeSetSyncTransaction},
         {"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
         {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 3248cf5..8ef3825 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -262,6 +262,7 @@
     optional int32 user_rotation = 3 [(.android.typedef) = "android.view.Surface.Rotation"];
     optional int32 fixed_to_user_rotation_mode = 4;
     optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
+    optional bool is_fixed_to_user_rotation = 6;
 }
 
 /* represents DockedTaskDividerController */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 007cf6b..6d96784 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1900,7 +1900,7 @@
          @hide This should only be used by ManagedProvisioning app.
     -->
     <permission android:name="android.permission.NETWORK_MANAGED_PROVISIONING"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows Carrier Provisioning to call methods in Networking services
          <p>Not for use by any other third-party or privileged applications.
@@ -2356,10 +2356,10 @@
     <permission android:name="android.permission.OEM_UNLOCK_STATE"
         android:protectionLevel="signature" />
 
-    <!-- @hide Allows querying state of PersistentDataBlock
+    <!-- @SystemApi @hide Allows querying state of PersistentDataBlock
    <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.ACCESS_PDB_STATE"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- Allows testing if a passwords is forbidden by the admins.
          @hide <p>Not for use by third-party applications. -->
@@ -2417,7 +2417,7 @@
     <!-- @SystemApi @TestApi Allows read access to privileged phone state.
          @hide Used internally. -->
     <permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- Allows to read device identifiers and use ICC based authentication like EAP-AKA.
          Often required in authentication to access the carrier's server and manage services
@@ -2733,25 +2733,25 @@
          user-targeted broadcasts.  This permission is not available to
          third party applications. -->
     <permission android:name="android.permission.INTERACT_ACROSS_USERS"
-        android:protectionLevel="signature|privileged|development" />
+        android:protectionLevel="signature|privileged|development|role" />
 
     <!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
          that removes restrictions on where broadcasts can be sent and allows other
          types of interactions
          @hide -->
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
-        android:protectionLevel="signature|installer" />
+        android:protectionLevel="signature|installer|role" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
     <!-- Allows interaction across profiles in the same profile group. -->
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
         android:protectionLevel="signature|appop" />
 
-    <!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can
-         interact across profiles in the same profile group.
+    <!-- @SystemApi Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that
+         they can interact across profiles in the same profile group.
          @hide -->
     <permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
          users on the device. This permission is not available to
@@ -2772,10 +2772,10 @@
     <permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
         android:protectionLevel="signature|privileged|development|role" />
 
-    <!-- @hide Allows an application to set the profile owners and the device owner.
+    <!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
          This permission is not available to third party applications.-->
     <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
-        android:protectionLevel="signature"
+        android:protectionLevel="signature|role"
         android:label="@string/permlab_manageProfileAndDeviceOwners"
         android:description="@string/permdesc_manageProfileAndDeviceOwners" />
 
@@ -2828,7 +2828,7 @@
 
     <!-- @SystemApi @hide Allows an application to start activities from background -->
     <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
-        android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" />
+        android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role" />
 
     <!-- Allows an application to start foreground services from the background at any time.
          <em>This permission is not for use by third-party applications</em>,
@@ -2990,6 +2990,17 @@
     <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- Allows application to request to be associated with a vehicle head unit capable of
+         automotive projection
+         ({@link android.companion.AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION})
+         by {@link android.companion.CompanionDeviceManager}.
+        <p>Not for use by third-party applications.
+         @hide
+         @SystemApi
+    -->
+    <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
+                android:protectionLevel="internal|role" />
+
     <!-- Allows a companion app to associate to Wi-Fi.
          <p>Only for use by a single pre-approved app.
          @hide
@@ -3054,7 +3065,7 @@
     <!-- Allows applications to set the system time directly.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.SET_TIME"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- Allows applications to set the system time zone directly.
          <p>Not for use by third-party applications.
@@ -3062,7 +3073,7 @@
     <permission android:name="android.permission.SET_TIME_ZONE"
         android:label="@string/permlab_setTimeZone"
         android:description="@string/permdesc_setTimeZone"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- Allows telephony to suggest the time / time zone.
          <p>Not for use by third-party applications.
@@ -3176,7 +3187,7 @@
          as locale.
          <p>Protection level: signature|privileged|development -->
     <permission android:name="android.permission.CHANGE_CONFIGURATION"
-        android:protectionLevel="signature|privileged|development" />
+        android:protectionLevel="signature|privileged|development|role" />
 
     <!-- Allows an application to read or write the system settings.
 
@@ -3193,7 +3204,7 @@
     <permission android:name="android.permission.WRITE_SETTINGS"
         android:label="@string/permlab_writeSettings"
         android:description="@string/permdesc_writeSettings"
-        android:protectionLevel="signature|preinstalled|appop|pre23" />
+        android:protectionLevel="signature|preinstalled|appop|pre23|role" />
 
     <!-- Allows an application to modify the Google service map.
     <p>Not for use by third-party applications. -->
@@ -3446,7 +3457,7 @@
     <!-- Allows an application to read or write the secure system settings.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
-        android:protectionLevel="signature|privileged|development" />
+        android:protectionLevel="signature|privileged|development|role" />
 
     <!-- Allows an application to retrieve state dump information from system services.
     <p>Not for use by third-party applications. -->
@@ -3706,7 +3717,7 @@
          to put the higher-level system there into a shutdown state.
          @hide -->
     <permission android:name="android.permission.SHUTDOWN"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- @SystemApi Allows an application to tell the activity manager to temporarily
          stop application switches, putting it into a special mode that
@@ -4077,14 +4088,13 @@
          <p>Protection level: signature
     -->
     <permission android:name="android.permission.BIND_DEVICE_ADMIN"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi Required to add or remove another application as a device admin.
          <p>Not for use by third-party applications.
-         @hide
-         @removed -->
+         @hide -->
     <permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|role" />
 
     <!-- @SystemApi Allows an app to reset the device password.
          <p>Not for use by third-party applications.
@@ -4197,14 +4207,14 @@
     <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows an application to install existing system packages. This is a limited
+    <!-- @SystemApi Allows an application to install existing system packages. This is a limited
          version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
          <p>Not for use by third-party applications.
          TODO(b/80204953): remove this permission once we have a long-term solution.
          @hide
     -->
     <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- Allows an application to use the package installer v2 APIs.
          <p>The package installer v2 APIs are still a work in progress and we're
@@ -4299,7 +4309,7 @@
          when the application deleting the package is not the same application that installed the
          package. -->
     <permission android:name="android.permission.DELETE_PACKAGES"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- @SystemApi Allows an application to move location of installed package.
          @hide -->
@@ -4315,7 +4325,7 @@
          enabled or not.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- @SystemApi Allows an application to grant specific permissions.
          @hide -->
@@ -4767,7 +4777,7 @@
 
     <!-- Not for use by third-party applications. -->
     <permission android:name="android.permission.MASTER_CLEAR"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- Allows an application to call any phone number, including emergency
          numbers, without going through the Dialer user interface for the user
@@ -4778,7 +4788,7 @@
 
     <!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
     <permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- @SystemApi Allows an application to perform SIM Activation @hide -->
     <permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
@@ -5005,7 +5015,7 @@
         @hide
     -->
     <permission android:name="android.permission.CRYPT_KEEPER"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|role" />
 
     <!-- @SystemApi Allows an application to read historical network usage for
          specific networks and applications. @hide -->
@@ -5150,10 +5160,10 @@
                 android:protectionLevel="signature|installer" />
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
 
-    <!-- Allows notifications to be colorized
+    <!-- @SystemApi Allows notifications to be colorized
          <p>Not for use by third-party applications. @hide -->
     <permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
-                android:protectionLevel="signature|setup" />
+                android:protectionLevel="signature|setup|role" />
 
     <!-- Allows access to keyguard secure storage.  Only allowed for system processes.
         @hide -->
@@ -5394,7 +5404,7 @@
     <!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
         @hide -->
     <permission android:name="android.permission.PEERS_MAC_ADDRESS"
-                android:protectionLevel="signature|setup" />
+                android:protectionLevel="signature|setup|role" />
 
     <!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
         can use this permission to ensure incoming Nfc messages are from the Nfc stack
@@ -5699,11 +5709,11 @@
     <permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
         android:protectionLevel="signature" />
 
-    <!-- Allows an app to mark a profile owner as managing an organization-owned device.
+    <!-- @SystemApi Allows an app to mark a profile owner as managing an organization-owned device.
          <p>Not for use by third-party applications.
          @hide -->
     <permission android:name="android.permission.MARK_DEVICE_ORGANIZATION_OWNED"
-                android:protectionLevel="signature" />
+                android:protectionLevel="signature|role" />
 
     <!-- Allows financial apps to read filtered sms messages.
          Protection level: signature|appop
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 755938e..7805d46 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2953,6 +2953,9 @@
              usually TVs.
              <p>Requires permission {@code android.permission.DISABLE_SYSTEM_SOUND_EFFECTS}. -->
         <attr name="playHomeTransitionSound" format="boolean"/>
+        <!-- Indicates whether the activity can be displayed on a remote device which may or
+             may not be running Android. -->
+        <attr name="canDisplayOnRemoteDevices" format="boolean"/>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e1b1e61..b924bd2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2051,6 +2051,8 @@
     <!-- The name of the package that will hold the television remote service role.
         TODO(b/189347385) make this a @SystemAPI -->
     <string name="config_systemTelevisionRemoteService" translatable="false">@string/config_tvRemoteServicePackage</string>
+    <!-- The name of the package that will hold the device management role -->
+    <string name="config_deviceManager" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 366dccb..2820f86 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3303,6 +3303,7 @@
     <public name="sharedUserMaxSdkVersion" />
     <public name="requiredSplitTypes" />
     <public name="splitTypes" />
+    <public name="canDisplayOnRemoteDevices" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
@@ -3312,12 +3313,14 @@
     <public name="accessibilityActionSwipeDown" />
   </staging-public-group>
 
-  <staging-public-group type="style" first-id="0x0dfd0000">
+  <staging-public-group type="style" first-id="0x01dd0000">
   </staging-public-group>
 
   <staging-public-group type="string" first-id="0x01dc0000">
     <!-- @hide @SystemApi -->
     <public name="config_systemSupervision" />
+    <!-- @hide @SystemApi -->
+    <public name="config_deviceManager" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 391c7dc..5f08125 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1495,7 +1495,7 @@
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50]-->
     <string name="permlab_nearby_wifi_devices">interact with nearby Wi\u2011Fi devices</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=140]-->
     <string name="permdesc_nearby_wifi_devices">Allows the app to advertise, connect, and determine the relative position of nearby Wi\u2011Fi devices</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java
index 4a1cdbf..160b20d 100644
--- a/core/tests/coretests/src/android/os/UserHandleTest.java
+++ b/core/tests/coretests/src/android/os/UserHandleTest.java
@@ -24,12 +24,15 @@
 import static android.os.UserHandle.getUserId;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Random;
+
 @RunWith(AndroidJUnit4.class)
 public class UserHandleTest {
     // NOTE: keep logic in sync with system/core/libcutils/tests/multiuser_test.cpp
@@ -117,4 +120,31 @@
     private static int multiuser_get_app_id(int uid) {
         return getAppId(uid);
     }
+
+    @Test
+    public void testExtraCache() {
+        assertEquals(0, UserHandle.sExtraUserHandleCache.size());
+
+        // This shouldn't hit the extra cache.
+        assertEquals(10, UserHandle.of(10).getIdentifier());
+
+        assertEquals(0, UserHandle.sExtraUserHandleCache.size());
+
+        // This should hit the cache
+        assertEquals(20, UserHandle.of(20).getIdentifier());
+
+        assertEquals(1, UserHandle.sExtraUserHandleCache.size());
+
+        // Make sure the cache works.
+        final Random rnd = new Random();
+        for (int i = 0; i < 10000; i++) {
+            final int userId = rnd.nextInt(100);
+            assertEquals(userId, UserHandle.of(userId).getIdentifier());
+            assertSame(UserHandle.of(userId), UserHandle.of(userId));
+        }
+
+        // Make sure the cache size doesn't exceed the max size.
+        assertEquals(UserHandle.MAX_EXTRA_USER_HANDLE_CACHE_SIZE,
+                UserHandle.sExtraUserHandleCache.size());
+    }
 }
diff --git a/data/etc/com.android.cellbroadcastreceiver.xml b/data/etc/com.android.cellbroadcastreceiver.xml
index 01a28a8..bc62bbc 100644
--- a/data/etc/com.android.cellbroadcastreceiver.xml
+++ b/data/etc/com.android.cellbroadcastreceiver.xml
@@ -19,6 +19,7 @@
         <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.STATUS_BAR"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 33cc61b..6983aa4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -49,6 +49,7 @@
         <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.STATUS_BAR"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 9af508a..405f66d 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -31,7 +31,7 @@
             long height, int format);
     private static native void nativeDestroy(long ptr);
     private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
-    private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
+    private static native void nativeSetSyncTransaction(long ptr, long transactionPtr);
     private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
             int format, long transactionPtr);
     private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
@@ -70,8 +70,8 @@
      * This gives the caller a chance to apply the transaction when it's ready.
      * @param t The transaction to add the frame to. This can be null to clear the transaction.
      */
-    public void setNextTransaction(@Nullable SurfaceControl.Transaction t) {
-        nativeSetNextTransaction(mNativeObject, t == null ? 0 : t.mNativeObject);
+    public void setSyncTransaction(@Nullable SurfaceControl.Transaction t) {
+        nativeSetSyncTransaction(mNativeObject, t == null ? 0 : t.mNativeObject);
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 1e9fda6..44af1a9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -73,17 +73,55 @@
     static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
         final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
         final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule)
-                && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary();
+                && ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary()
+                != SplitRule.FINISH_NEVER;
         return shouldFinishPrimaryWithSecondary || isPlaceholderContainer;
     }
 
     static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) {
         final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
         final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule)
-                && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary();
+                && ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary()
+                != SplitRule.FINISH_NEVER;
         return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
     }
 
+    static boolean shouldFinishAssociatedContainerWhenStacked(int finishBehavior) {
+        return finishBehavior == SplitRule.FINISH_ALWAYS;
+    }
+
+    static boolean shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior) {
+        return finishBehavior == SplitRule.FINISH_ALWAYS
+                || finishBehavior == SplitRule.FINISH_ADJACENT;
+    }
+
+    static int getFinishPrimaryWithSecondaryBehavior(@NonNull SplitRule splitRule) {
+        if (splitRule instanceof SplitPlaceholderRule) {
+            return ((SplitPlaceholderRule) splitRule).getFinishPrimaryWithSecondary();
+        }
+        if (splitRule instanceof SplitPairRule) {
+            return ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary();
+        }
+        return SplitRule.FINISH_NEVER;
+    }
+
+    static int getFinishSecondaryWithPrimaryBehavior(@NonNull SplitRule splitRule) {
+        if (splitRule instanceof SplitPlaceholderRule) {
+            return SplitRule.FINISH_ALWAYS;
+        }
+        if (splitRule instanceof SplitPairRule) {
+            return ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary();
+        }
+        return SplitRule.FINISH_NEVER;
+    }
+
+    static boolean isStickyPlaceholderRule(@NonNull SplitRule splitRule) {
+        if (!(splitRule instanceof SplitPlaceholderRule)) {
+            return false;
+        }
+        return ((SplitPlaceholderRule) splitRule).isSticky();
+    }
+
     @Override
     public String toString() {
         return "SplitContainer{"
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 9014102..68c1904 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -16,6 +16,12 @@
 
 package androidx.window.extensions.embedding;
 
+import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -460,6 +466,11 @@
             return false;
         }
 
+        if (isStickyPlaceholderRule(splitContainer.getSplitRule())) {
+            // The placeholder should remain after it was first shown.
+            return false;
+        }
+
         if (mPresenter.shouldShowSideBySide(splitContainer)) {
             return false;
         }
@@ -643,6 +654,52 @@
         return false;
     }
 
+    /**
+     * Checks whether the associated container should be destroyed together with a finishing
+     * container. There is a case when primary containers for placeholders should be retained
+     * despite the rule configuration to finish primary with secondary - if they are marked as
+     * 'sticky' and the placeholder was finished when fully overlapping the primary container.
+     * @return {@code true} if the associated container should be retained (and not be finished).
+     */
+    boolean shouldRetainAssociatedContainer(@NonNull TaskFragmentContainer finishingContainer,
+            @NonNull TaskFragmentContainer associatedContainer) {
+        SplitContainer splitContainer = getActiveSplitForContainers(associatedContainer,
+                finishingContainer);
+        if (splitContainer == null) {
+            // Containers are not in the same split, no need to retain.
+            return false;
+        }
+        // Find the finish behavior for the associated container
+        int finishBehavior;
+        SplitRule splitRule = splitContainer.getSplitRule();
+        if (finishingContainer == splitContainer.getPrimaryContainer()) {
+            finishBehavior = getFinishSecondaryWithPrimaryBehavior(splitRule);
+        } else {
+            finishBehavior = getFinishPrimaryWithSecondaryBehavior(splitRule);
+        }
+        // Decide whether the associated container should be retained based on the current
+        // presentation mode.
+        if (mPresenter.shouldShowSideBySide(splitContainer)) {
+            return !shouldFinishAssociatedContainerWhenAdjacent(finishBehavior);
+        } else {
+            return !shouldFinishAssociatedContainerWhenStacked(finishBehavior);
+        }
+    }
+
+    /**
+     * @see #shouldRetainAssociatedContainer(TaskFragmentContainer, TaskFragmentContainer)
+     */
+    boolean shouldRetainAssociatedActivity(@NonNull TaskFragmentContainer finishingContainer,
+            @NonNull Activity associatedActivity) {
+        TaskFragmentContainer associatedContainer = getContainerWithActivity(
+                associatedActivity.getActivityToken());
+        if (associatedContainer == null) {
+            return false;
+        }
+
+        return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
+    }
+
     private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
 
         @Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 6805fde..a1a53bc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -228,6 +228,9 @@
 
         // Finish dependent containers
         for (TaskFragmentContainer container : mContainersToFinishOnExit) {
+            if (controller.shouldRetainAssociatedContainer(this, container)) {
+                continue;
+            }
             container.finish(true /* shouldFinishDependent */, presenter,
                     wct, controller);
         }
@@ -235,6 +238,9 @@
 
         // Finish associated activities
         for (Activity activity : mActivitiesToFinishOnExit) {
+            if (controller.shouldRetainAssociatedActivity(this, activity)) {
+                continue;
+            }
             activity.finish();
         }
         mActivitiesToFinishOnExit.clear();
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 830d13d..d6678bf 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 9fe0247..7dc2f31 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -65,25 +65,28 @@
     <LinearLayout
         android:id="@+id/top_end_container"
         android:layout_gravity="top|end"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal">
+
         <ImageButton
             android:id="@+id/settings"
             android:layout_width="@dimen/pip_action_size"
             android:layout_height="@dimen/pip_action_size"
             android:contentDescription="@string/pip_phone_settings"
+            android:layout_gravity="top|start"
             android:gravity="center"
             android:src="@drawable/pip_ic_settings"
             android:background="?android:selectableItemBackgroundBorderless" />
 
         <ImageButton
-            android:id="@+id/dismiss"
+            android:id="@+id/enter_split"
             android:layout_width="@dimen/pip_action_size"
             android:layout_height="@dimen/pip_action_size"
-            android:contentDescription="@string/pip_phone_close"
+            android:layout_gravity="top|start"
             android:gravity="center"
-            android:src="@drawable/pip_ic_close_white"
+            android:contentDescription="@string/pip_phone_enter_split"
+            android:src="@drawable/pip_expand"
             android:background="?android:selectableItemBackgroundBorderless" />
     </LinearLayout>
 
@@ -97,4 +100,14 @@
         android:padding="@dimen/pip_resize_handle_padding"
         android:src="@drawable/pip_resize_handle"
         android:background="?android:selectableItemBackgroundBorderless" />
+
+    <ImageButton
+        android:id="@+id/dismiss"
+        android:layout_width="@dimen/pip_action_size"
+        android:layout_height="@dimen/pip_action_size"
+        android:contentDescription="@string/pip_phone_close"
+        android:layout_gravity="top|end"
+        android:gravity="center"
+        android:src="@drawable/pip_ic_close_white"
+        android:background="?android:selectableItemBackgroundBorderless" />
 </FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 764854a..c88fc16 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -24,6 +24,9 @@
     <!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
     <string name="pip_phone_settings">Settings</string>
 
+    <!-- Label for the PIP enter split button [CHAR LIMIT=NONE] -->
+    <string name="pip_phone_enter_split">Enter split screen</string>
+
     <!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
     <string name="pip_menu_title">Menu</string>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index f7af4e1..caa5327 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1036,10 +1036,9 @@
                 // notification, so that the bubble will be re-created if shouldBubbleUp returns
                 // true.
                 mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
-            } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble
-                    && !entry.getRanking().isSuspended()) {
+            } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
                 entry.setFlagBubble(true);
-                onEntryUpdated(entry, true /* shouldBubbleUp */);
+                onEntryUpdated(entry, shouldBubbleUp && !entry.getRanking().isSuspended());
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index b80dcd0..711a0ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -43,6 +43,7 @@
 import com.android.wm.shell.pip.tv.TvPipMenuController;
 import com.android.wm.shell.pip.tv.TvPipNotificationController;
 import com.android.wm.shell.pip.tv.TvPipTransition;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.Optional;
@@ -160,13 +161,14 @@
             PipTransitionController pipTransitionController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<LegacySplitScreenController> splitScreenOptional,
+            Optional<SplitScreenController> newSplitScreenOptional,
             DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
                 syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
                 tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
-                pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
-                shellTaskOrganizer, mainExecutor);
+                pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 944dfed..ec70147 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -55,6 +55,7 @@
 import com.android.wm.shell.pip.phone.PipController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
 import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
 import com.android.wm.shell.transition.Transitions;
@@ -215,14 +216,15 @@
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             PipTransitionController pipTransitionController,
             Optional<LegacySplitScreenController> splitScreenOptional,
+            Optional<SplitScreenController> newSplitScreenOptional,
             DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new PipTaskOrganizer(context,
                 syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
                 menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
-                pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
-                shellTaskOrganizer, mainExecutor);
+                pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+                displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b6e5804..6cc5f09 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -77,6 +77,7 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
@@ -126,7 +127,8 @@
     private final int mExitAnimationDuration;
     private final int mCrossFadeAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
-    private final Optional<LegacySplitScreenController> mSplitScreenOptional;
+    private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
+    private final Optional<SplitScreenController> mSplitScreenOptional;
     protected final ShellTaskOrganizer mTaskOrganizer;
     protected final ShellExecutor mMainExecutor;
 
@@ -252,7 +254,8 @@
             @NonNull PipAnimationController pipAnimationController,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
             @NonNull PipTransitionController pipTransitionController,
-            Optional<LegacySplitScreenController> splitScreenOptional,
+            Optional<LegacySplitScreenController> legacySplitScreenOptional,
+            Optional<SplitScreenController> splitScreenOptional,
             @NonNull DisplayController displayController,
             @NonNull PipUiEventLogger pipUiEventLogger,
             @NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -274,6 +277,7 @@
         mPipAnimationController = pipAnimationController;
         mPipUiEventLoggerLogger = pipUiEventLogger;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+        mLegacySplitScreenOptional = legacySplitScreenOptional;
         mSplitScreenOptional = splitScreenOptional;
         mTaskOrganizer = shellTaskOrganizer;
         mMainExecutor = mainExecutor;
@@ -373,8 +377,11 @@
      *   activity render it's final configuration while the Task is still in PiP.
      * - setWindowingMode to undefined at the end of transition
      * @param animationDurationMs duration in millisecond for the exiting PiP transition
+     * @param requestEnterSplit whether the enterSplit button is pressed on PiP or not.
+     *                             Indicate the user wishes to directly put PiP into split screen
+     *                             mode.
      */
-    public void exitPip(int animationDurationMs) {
+    public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
         if (!mPipTransitionState.isInPip()
                 || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
                 || mToken == null) {
@@ -387,7 +394,7 @@
                 PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
-        final int direction = syncWithSplitScreenBounds(destinationBounds)
+        final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
                 ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
                 : TRANSITION_DIRECTION_LEAVE_PIP;
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -396,7 +403,7 @@
         // We set to fullscreen here for now, but later it will be set to UNDEFINED for
         // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
         wct.setActivityWindowingMode(mToken,
-                direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+                direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
                         ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                         : WINDOWING_MODE_FULLSCREEN);
         wct.setBounds(mToken, destinationBounds);
@@ -435,7 +442,7 @@
         wct.setWindowingMode(mToken, getOutPipWindowingMode());
         // Simply reset the activity mode set prior to the animation running.
         wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
-        mSplitScreenOptional.ifPresent(splitScreen -> {
+        mLegacySplitScreenOptional.ifPresent(splitScreen -> {
             if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
                 wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
             }
@@ -1165,6 +1172,7 @@
             @PipAnimationController.TransitionDirection int direction,
             @PipAnimationController.AnimationType int type) {
         final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
+        final boolean isPipTopLeft = isPipTopLeft();
         mPipBoundsState.setBounds(destinationBounds);
         if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
             removePipImmediately();
@@ -1210,10 +1218,10 @@
                             null /* callback */, false /* withStartDelay */);
                 });
             } else {
-                applyFinishBoundsResize(wct, direction);
+                applyFinishBoundsResize(wct, direction, isPipTopLeft);
             }
         } else {
-            applyFinishBoundsResize(wct, direction);
+            applyFinishBoundsResize(wct, direction, isPipTopLeft);
         }
 
         finishResizeForMenu(destinationBounds);
@@ -1241,7 +1249,11 @@
         } else if (isOutPipDirection(direction)) {
             // If we are animating to fullscreen or split screen, then we need to reset the
             // override bounds on the task to ensure that the task "matches" the parent's bounds.
-            taskBounds = null;
+            if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+                taskBounds = destinationBounds;
+            } else {
+                taskBounds = null;
+            }
             applyWindowingModeChangeOnExit(wct, direction);
         } else {
             // Just a resize in PIP
@@ -1261,8 +1273,20 @@
      * applying it.
      */
     public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
-            @PipAnimationController.TransitionDirection int direction) {
-        mTaskOrganizer.applyTransaction(wct);
+            @PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
+        if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+            mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+        } else {
+            mTaskOrganizer.applyTransaction(wct);
+        }
+    }
+
+    private boolean isPipTopLeft() {
+        final Rect topLeft = new Rect();
+        final Rect bottomRight = new Rect();
+        mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+
+        return topLeft.contains(mPipBoundsState.getBounds());
     }
 
     /**
@@ -1347,18 +1371,27 @@
     }
 
     /**
-     * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
-     * screen.
+     * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
+     * bounds if PiP is going to split screen.
      *
      * @param destinationBoundsOut contain the updated destination bounds if applicable
      * @return {@code true} if destinationBounds is altered for split screen
      */
-    private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
-        if (!mSplitScreenOptional.isPresent()) {
+    private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
+        if (enterSplit && mSplitScreenOptional.isPresent()) {
+            final Rect topLeft = new Rect();
+            final Rect bottomRight = new Rect();
+            mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+            final boolean isPipTopLeft = isPipTopLeft();
+            destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
+            return true;
+        }
+
+        if (!mLegacySplitScreenOptional.isPresent()) {
             return false;
         }
 
-        LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
+        LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
         if (!legacySplitScreen.isDividerVisible()) {
             // fail early if system is not in split screen mode
             return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index ae8c1b6..5687f4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -95,6 +95,11 @@
          * Called when the PIP requested to show the menu.
          */
         void onPipShowMenu();
+
+        /**
+         * Called when the PIP requested to enter Split.
+         */
+        void onEnterSplit();
     }
 
     private final Matrix mMoveTransform = new Matrix();
@@ -458,6 +463,10 @@
         mListeners.forEach(Listener::onPipDismiss);
     }
 
+    void onEnterSplit() {
+        mListeners.forEach(Listener::onEnterSplit);
+    }
+
     /**
      * @return the best set of actions to show in the PiP menu.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 47a8c67..69ae45d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -151,7 +151,7 @@
                         result = true;
                         break;
                     case AccessibilityNodeInfo.ACTION_EXPAND:
-                        mMotionHelper.expandLeavePip();
+                        mMotionHelper.expandLeavePip(false /* skipAnimation */);
                         result = true;
                         break;
                     default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a32eb16..aeeb73f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -482,7 +482,8 @@
                     false /* fromShelfAdjustment */,
                     wct /* windowContainerTransaction */);
             if (wct != null) {
-                mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
+                mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME,
+                        false /* wasPipTopLeft */);
             }
         };
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 3eeba6e..0644657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -18,8 +18,6 @@
 
 import android.content.Context;
 import android.graphics.Rect;
-import android.util.Log;
-import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -34,6 +32,7 @@
     protected ViewGroup mViewRoot;
     protected ViewGroup mTopEndContainer;
     protected View mDragHandle;
+    protected View mEnterSplitButton;
     protected View mSettingsButton;
     protected View mDismissButton;
 
@@ -44,14 +43,13 @@
      * Bind the necessary views.
      */
     public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
-            View settingsButton, View dismissButton) {
+            View enterSplitButton, View settingsButton, View dismissButton) {
         mViewRoot = viewRoot;
         mTopEndContainer = topEndContainer;
         mDragHandle = dragHandle;
+        mEnterSplitButton = enterSplitButton;
         mSettingsButton = settingsButton;
         mDismissButton = dismissButton;
-
-        bindInitialViewState();
     }
 
     /**
@@ -72,22 +70,4 @@
             v.setLayoutParams(params);
         }
     }
-
-    /** Calculate the initial state of the menu icons. Called when the menu is first created. */
-    private void bindInitialViewState() {
-        if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
-                || mSettingsButton == null || mDismissButton == null) {
-            Log.e(TAG, "One of the required views is null.");
-            return;
-        }
-        // The menu view layout starts out with the settings button aligned at the top|end of the
-        // view group next to the dismiss button. On phones, the settings button should be aligned
-        // to the top|start of the view, so move it to parent view group to then align it to the
-        // top|start of the menu.
-        mTopEndContainer.removeView(mSettingsButton);
-        mViewRoot.addView(mSettingsButton);
-
-        setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
-        setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 8ef2b6b..7bbebe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -99,7 +99,7 @@
     private static final float MENU_BACKGROUND_ALPHA = 0.3f;
     private static final float DISABLED_ACTION_ALPHA = 0.54f;
 
-    private static final boolean ENABLE_RESIZE_HANDLE = false;
+    private static final boolean ENABLE_ENTER_SPLIT = false;
 
     private int mMenuState;
     private boolean mAllowMenuTimeout = true;
@@ -139,7 +139,7 @@
     protected View mViewRoot;
     protected View mSettingsButton;
     protected View mDismissButton;
-    protected View mResizeHandle;
+    protected View mEnterSplitButton;
     protected View mTopEndContainer;
     protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
 
@@ -177,14 +177,23 @@
             }
         });
 
-        mResizeHandle = findViewById(R.id.resize_handle);
-        mResizeHandle.setAlpha(0);
+        mEnterSplitButton = findViewById(R.id.enter_split);
+        mEnterSplitButton.setAlpha(0);
+        mEnterSplitButton.setOnClickListener(v -> {
+            if (mMenuContainer.getAlpha() != 0) {
+                enterSplit();
+            }
+        });
+
+        findViewById(R.id.resize_handle).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);
+                findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton,
+                mDismissButton);
         mDismissFadeOutDurationMs = context.getResources()
                 .getInteger(R.integer.config_pipExitAnimationDuration);
 
@@ -268,14 +277,13 @@
                     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 && showResizeHandle ? 1f : 0f);
+            ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+                    mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
             if (menuState == MENU_STATE_FULL) {
                 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
-                        resizeAnim);
+                        enterSplitAnim);
             } else {
-                mMenuContainerAnimator.playTogether(resizeAnim);
+                mMenuContainerAnimator.playTogether(enterSplitAnim);
             }
             mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
             mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -328,7 +336,7 @@
         mMenuContainer.setAlpha(0f);
         mSettingsButton.setAlpha(0f);
         mDismissButton.setAlpha(0f);
-        mResizeHandle.setAlpha(0f);
+        mEnterSplitButton.setAlpha(0f);
     }
 
     void pokeMenu() {
@@ -368,9 +376,10 @@
                     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);
+            ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+                    mEnterSplitButton.getAlpha(), 0f);
+            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+                    enterSplitAnim);
             mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
             mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType));
             mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@@ -522,6 +531,14 @@
         }
     }
 
+    private void enterSplit() {
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */,
+                ANIM_TYPE_HIDE);
+    }
+
+
     private void showSettings() {
         final Pair<ComponentName, Integer> topPipActivityInfo =
                 PipUtils.getTopPipActivity(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index dbd09fd..c634b7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -338,22 +338,29 @@
      * Resizes the pinned stack back to unknown windowing mode, which could be freeform or
      *      * fullscreen depending on the display area's windowing mode.
      */
-    void expandLeavePip() {
-        expandLeavePip(false /* skipAnimation */);
+    void expandLeavePip(boolean skipAnimation) {
+        expandLeavePip(skipAnimation, false /* enterSplit */);
+    }
+
+    /**
+     * Resizes the pinned task to split-screen mode.
+     */
+    void expandIntoSplit() {
+        expandLeavePip(false, true /* enterSplit */);
     }
 
     /**
      * Resizes the pinned stack back to unknown windowing mode, which could be freeform or
      * fullscreen depending on the display area's windowing mode.
      */
-    void expandLeavePip(boolean skipAnimation) {
+    private void expandLeavePip(boolean skipAnimation, boolean enterSplit) {
         if (DEBUG) {
             Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
         cancelPhysicsAnimation();
         mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
-        mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
+        mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9f2f6a5..570fd5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -139,7 +139,12 @@
 
         @Override
         public void onPipExpand() {
-            mMotionHelper.expandLeavePip();
+            mMotionHelper.expandLeavePip(false /* skipAnimation */);
+        }
+
+        @Override
+        public void onEnterSplit() {
+            mMotionHelper.expandIntoSplit();
         }
 
         @Override
@@ -899,7 +904,7 @@
                     // Expand to fullscreen if this is a double tap
                     // the PiP should be frozen until the transition ends
                     setTouchEnabled(false);
-                    mMotionHelper.expandLeavePip();
+                    mMotionHelper.expandLeavePip(false /* skipAnimation */);
                 }
             } else if (mMenuState != MENU_STATE_FULL) {
                 if (mPipBoundsState.isStashed()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index a2e9b64..00083d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -219,7 +219,7 @@
     public void movePipToFullscreen() {
         if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState));
 
-        mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
+        mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
         onPipDisappeared();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 04058ed..7457be2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -202,11 +202,25 @@
         return moveToSideStage(task, sideStagePosition);
     }
 
+    public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
+            WindowContainerTransaction wct) {
+        final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+        if (task == null) {
+            throw new IllegalArgumentException("Unknown taskId" + taskId);
+        }
+        return moveToSideStage(task, sideStagePosition, wct);
+    }
+
     public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
             @SplitPosition int sideStagePosition) {
         return mStageCoordinator.moveToSideStage(task, sideStagePosition);
     }
 
+    public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+            @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
+        return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
+    }
+
     public boolean removeFromSideStage(int taskId) {
         return mStageCoordinator.removeFromSideStage(taskId);
     }
@@ -224,6 +238,11 @@
                 leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
     }
 
+    public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
+        moveToSideStage(taskId,
+                leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+    }
+
     public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
         mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 3589f7c..95886c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -281,6 +281,11 @@
     boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
             @SplitPosition int sideStagePosition) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        return moveToSideStage(task, sideStagePosition, wct);
+    }
+
+    boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+            @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         setSideStagePosition(sideStagePosition, wct);
         mSideStage.evictAllChildren(evictWct);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0270093..0172cf32 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -50,6 +50,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -75,7 +76,8 @@
     @Mock private PipTransitionController mMockPipTransitionController;
     @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
     @Mock private PipUiEventLogger mMockPipUiEventLogger;
-    @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
+    @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
+    @Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
     @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
     private TestShellExecutor mMainExecutor;
     private PipBoundsState mPipBoundsState;
@@ -99,8 +101,9 @@
                 mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
                 mPipBoundsAlgorithm, mMockPhonePipMenuController,
                 mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
-                mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
-                mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+                mMockPipTransitionController, mMockOptionalLegacySplitScreen,
+                mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
+                mMockShellTaskOrganizer, mMainExecutor));
         mMainExecutor.flushAll();
         preparePipTaskOrg();
     }
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index 9bbd0a9..29ef2b8 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -34,6 +34,8 @@
  */
 class FunctorDrawable : public SkDrawable {
 public:
+    constexpr static const char* const TYPE_NAME = "FunctorDrawable";
+
     FunctorDrawable(int functor, SkCanvas* canvas)
             : mBounds(canvas->getLocalClipBounds())
             , mWebViewHandle(WebViewFunctorManager::instance().handleFor(functor)) {}
@@ -48,6 +50,8 @@
         mWebViewHandle->onRemovedFromTree();
     }
 
+    const char* getTypeName() const override { return TYPE_NAME; }
+
 protected:
     virtual SkRect onGetBounds() override { return mBounds; }
 
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 6777c00..41e3687 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 #include "TransformCanvas.h"
+
+#include "FunctorDrawable.h"
 #include "HolePunch.h"
 #include "SkData.h"
 #include "SkDrawable.h"
@@ -35,7 +37,17 @@
 }
 
 void TransformCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
-    drawable->draw(this, matrix);
+    // TransformCanvas filters all drawing commands while maintaining the current
+    // clip stack and transformation. We need to draw most SkDrawables, since their
+    // draw calls may call methods that affect the clip stack and transformation. (Any
+    // actual draw commands will then be filtered out.) But FunctorDrawables are used
+    // as leaf nodes which issue self-contained OpenGL/Vulkan commands. These won't
+    // affect the clip stack + transformation, and in some cases cause problems (e.g. if
+    // the surface only has an alpha channel). See b/203960959
+    const auto* drawableName = drawable->getTypeName();
+    if (drawableName == nullptr || strcmp(drawableName, FunctorDrawable::TYPE_NAME) != 0) {
+        drawable->draw(this, matrix);
+    }
 }
 
 bool TransformCanvas::onFilter(SkPaint& paint) const {
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index ad62f6e..a347345 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -18,6 +18,4 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.settingslib">
 
-    <uses-sdk android:minSdkVersion="29" />
-
 </manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 5817f77..6e0d827 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -18,8 +18,4 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.settingslib.widget">
 
-    <uses-sdk
-        android:minSdkVersion="28"
-        android:targetSdkVersion="31"/>
-
 </manifest>
diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml
index fd89676..e9957a9 100644
--- a/packages/SettingsLib/Utils/AndroidManifest.xml
+++ b/packages/SettingsLib/Utils/AndroidManifest.xml
@@ -18,6 +18,4 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.settingslib.utils">
 
-    <uses-sdk android:minSdkVersion="21" />
-
 </manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
index ba63a8e..0416b68 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
@@ -52,7 +52,6 @@
     public final void setupLearnMoreButtonToShowAdminPolicies(Context context,
             int enforcementAdminUserId, EnforcedAdmin enforcedAdmin) {
         requireNonNull(context, "context cannot be null");
-        requireNonNull(enforcedAdmin, "enforcedAdmin cannot be null");
 
         // The "Learn more" button appears only if the restriction is enforced by an admin in the
         // same profile group or by the device owner. Otherwise the admin package and its policies
@@ -132,7 +131,7 @@
     }
 
     private void showAdminPolicies(Context context, EnforcedAdmin enforcedAdmin) {
-        if (enforcedAdmin.component != null) {
+        if (enforcedAdmin != null && enforcedAdmin.component != null) {
             launchShowAdminPolicies(context, enforcedAdmin.user, enforcedAdmin.component);
         } else {
             launchShowAdminSettings(context);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 713faaa..986fd45 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -303,9 +303,6 @@
     <!-- This value is used for the default system capabilities used on LE device. -->
     <string name="def_wearable_leSystemCapabilities" translatable="false">1</string>
 
-    <!-- Brightness levels, on a 0-255 scale -->
-    <string name="def_wearable_brightnessLevels" translatable="false">255,204,153,102,51</string>
-
     <!-- Whether to allow mobile signal detector by default. -->
     <bool name="def_wearable_mobileSignalDetectorAllowed">true</bool>
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 96f127b..fb6c248 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -42,6 +42,7 @@
         Settings.Secure.AUTOFILL_SERVICE,
         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+        Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
         Settings.Secure.ENABLED_VR_LISTENERS,
         Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
         Settings.Secure.TOUCH_EXPLORATION_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index bf8b933..7859159 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -66,6 +66,8 @@
                 new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE));
         VALIDATORS.put(
                 Secure.ENABLED_ACCESSIBILITY_SERVICES, COLON_SEPARATED_COMPONENT_LIST_VALIDATOR);
+        VALIDATORS.put(
+                Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ENABLED_VR_LISTENERS, COLON_SEPARATED_COMPONENT_LIST_VALIDATOR);
         VALIDATORS.put(
                 Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index cd4047b..527fd88 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -5294,11 +5294,6 @@
                             Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
                             SystemProperties.getInt("ro.cw_build.platform_mr", 0));
                     initGlobalSettingsDefaultValForWearLocked(
-                            Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
-                            getContext()
-                                    .getResources()
-                                    .getString(R.string.def_wearable_brightnessLevels));
-                    initGlobalSettingsDefaultValForWearLocked(
                             Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
                             getContext()
                                     .getResources()
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c05e01d..53df2e82 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -272,7 +272,6 @@
                     Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
                     Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
                     Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
-                    Settings.Global.ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
                     Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT,
                     Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
                     Settings.Global.ENHANCED_4G_MODE_ENABLED,
@@ -632,7 +631,6 @@
                     Settings.Global.Wearable.SYSTEM_CAPABILITIES,
                     Settings.Global.Wearable.SYSTEM_EDITION,
                     Settings.Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
-                    Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
                     Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
                     Settings.Global.Wearable.AMBIENT_ENABLED,
                     Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE,
@@ -710,6 +708,7 @@
                  Settings.Secure.DOCKED_CLOCK_FACE,
                  Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                  Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
+                 Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
                  Settings.Secure.ENABLED_INPUT_METHODS,  // Intentionally removed in P
                  Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
                  Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e1e9420..dde0a19 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -294,9 +294,6 @@
 
     <uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
 
-    <!-- Permission for dream overlay. -->
-    <uses-permission android:name="android.permission.BIND_DREAM_SERVICE" />
-
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
 
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index ce23a8b..e1da744 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,6 +11,7 @@
 awickham@google.com
 beverlyt@google.com
 brockman@google.com
+brycelee@google.com
 ccassidy@google.com
 cinek@google.com
 cwren@google.com
@@ -71,4 +72,4 @@
 hseog@google.com
 
 #Android TV
-rgl@google.com
\ No newline at end of file
+rgl@google.com
diff --git a/packages/SystemUI/plugin/AndroidManifest.xml b/packages/SystemUI/plugin/AndroidManifest.xml
index 7c057dc..811595ad 100644
--- a/packages/SystemUI/plugin/AndroidManifest.xml
+++ b/packages/SystemUI/plugin/AndroidManifest.xml
@@ -18,7 +18,4 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.systemui.plugins">
 
-    <uses-sdk
-        android:minSdkVersion="21" />
-
 </manifest>
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index a110413..07c2ac4 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -5,8 +5,8 @@
 -keep class com.android.systemui.car.CarSystemUIFactory
 -keep class com.android.systemui.SystemUIFactory
 -keep class com.android.systemui.tv.TvSystemUIFactory
--keep class * extends com.android.systemui.SystemUI
--keep class * implements com.android.systemui.SystemUI$Injector
+-keep class * extends com.android.systemui.CoreStartable
+-keep class * implements com.android.systemui.CoreStartable$Injector
 
 -keepclasseswithmembers class * {
     public <init>(android.content.Context, android.util.AttributeSet);
@@ -22,7 +22,7 @@
 }
 -keep class androidx.core.app.CoreComponentFactory
 
--keep public class * extends com.android.systemui.SystemUI {
+-keep public class * extends com.android.systemui.CoreStartable {
     public <init>(android.content.Context);
 }
 
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 6bc0138..b2ff46e 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -78,12 +78,6 @@
                     android:layout_gravity="center_vertical"
                     android:padding="8dp" />
             </com.android.systemui.statusbar.notification.row.AppControlView>
-            <!-- divider view -->
-            <View
-                android:layout_width="match_parent"
-                android:layout_height="1dp"
-                android:background="@*android:color/background_device_default_light"
-            />
 
             <!-- ChannelRows get added dynamically -->
 
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index 245d157..d03cd7e 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -87,10 +87,4 @@
             android:padding="8dp"
         />
     </LinearLayout>
-    <!-- divider view -->
-    <View
-        android:layout_width="match_parent"
-        android:layout_height=".5dp"
-        android:background="@*android:color/background_device_default_light"
-    />
 </com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d2ea789..af5d85d 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -77,7 +77,7 @@
     <!-- The color of the gear shown behind a notification -->
     <color name="notification_gear_color">@color/GM2_grey_700</color>
 
-    <color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color>
+    <color name="notification_guts_link_icon_tint">@*android:color/accent_device_default</color>
     <color name="notification_guts_sub_text_color">@color/GM2_grey_700</color>
     <color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
     <color name="notification_guts_priority_button_content_color">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fc6b99a..cbf3e83 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -638,9 +638,6 @@
          screen on inactivity. -->
     <bool name="config_enableIdleMode">false</bool>
 
-    <!-- Timeout to idle mode duration in milliseconds. -->
-    <integer name="config_idleModeTimeout">10000</integer>
-
     <!-- This value is used when calculating whether the device is in ambient light mode. It is
         light mode when the light sensor sample value exceeds above this value. -->
     <integer name="config_ambientLightModeThreshold">5</integer>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 974eda7..d972b7fc 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -213,8 +213,6 @@
         <item name="android:paddingTop">12dp</item>
         <item name="android:paddingHorizontal">24dp</item>
         <item name="android:textSize">24sp</item>
-        <item name="android:singleLine">true</item>
-        <item name="android:ellipsize">marquee</item>
     </style>
 
     <style name="TextAppearance.AuthCredential.Subtitle">
@@ -222,8 +220,6 @@
         <item name="android:paddingTop">8dp</item>
         <item name="android:paddingHorizontal">24dp</item>
         <item name="android:textSize">16sp</item>
-        <item name="android:singleLine">true</item>
-        <item name="android:ellipsize">marquee</item>
     </style>
 
     <style name="TextAppearance.AuthCredential.Description">
@@ -231,8 +227,6 @@
         <item name="android:paddingTop">8dp</item>
         <item name="android:paddingHorizontal">24dp</item>
         <item name="android:textSize">14sp</item>
-        <item name="android:singleLine">true</item>
-        <item name="android:ellipsize">marquee</item>
     </style>
 
     <style name="TextAppearance.AuthCredential.Error">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
index ee6dea5..91a3912 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
@@ -20,6 +20,11 @@
  */
 interface FlagReader {
     /** Returns a boolean value for the given flag.  */
+    fun isEnabled(flag: BooleanFlag): Boolean {
+        return flag.default
+    }
+
+    /** Returns a boolean value for the given flag.  */
     fun isEnabled(id: Int, def: Boolean): Boolean {
         return def
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3f2ff74..3128ffd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -23,17 +23,14 @@
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.ViewDebug;
 
-import com.android.systemui.shared.recents.utilities.Utilities;
+import androidx.annotation.Nullable;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Objects;
 
 /**
@@ -202,8 +199,8 @@
      * The icon is the task description icon (if provided), which falls back to the activity icon,
      * which can then fall back to the application icon.
      */
-    public Drawable icon;
-    public ThumbnailData thumbnail;
+    @Nullable public Drawable icon;
+    @Nullable public ThumbnailData thumbnail;
     @ViewDebug.ExportedProperty(category="recents")
     @Deprecated
     public String title;
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
index ef04619..acfa3c8 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -26,13 +26,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.util.Log;
 
+import androidx.annotation.BoolRes;
 import androidx.annotation.NonNull;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -62,14 +65,19 @@
 
     private final FlagManager mFlagManager;
     private final SecureSettings mSecureSettings;
+    private final Resources mResources;
     private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
 
     @Inject
-    public FeatureFlagManager(FlagManager flagManager,
-            SecureSettings secureSettings, Context context,
+    public FeatureFlagManager(
+            FlagManager flagManager,
+            Context context,
+            SecureSettings secureSettings,
+            @Main Resources resources,
             DumpManager dumpManager) {
         mFlagManager = flagManager;
         mSecureSettings = secureSettings;
+        mResources = resources;
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_SET_FLAG);
         filter.addAction(ACTION_GET_FLAGS);
@@ -77,17 +85,32 @@
         dumpManager.registerDumpable(TAG, this);
     }
 
-    /** Return a {@link BooleanFlag}'s value. */
     @Override
-    public boolean isEnabled(int id, boolean defaultValue) {
+    public boolean isEnabled(BooleanFlag flag) {
+        int id = flag.getId();
         if (!mBooleanFlagCache.containsKey(id)) {
-            Boolean result = isEnabledInternal(id);
-            mBooleanFlagCache.put(id, result == null ? defaultValue : result);
+            boolean def = flag.getDefault();
+            if (flag.hasResourceOverride()) {
+                try {
+                    def = isEnabledInOverlay(flag.getResourceOverride());
+                } catch (Resources.NotFoundException e) {
+                    // no-op
+                }
+            }
+
+            mBooleanFlagCache.put(id, isEnabled(id, def));
         }
 
         return mBooleanFlagCache.get(id);
     }
 
+    /** Return a {@link BooleanFlag}'s value. */
+    @Override
+    public boolean isEnabled(int id, boolean defaultValue) {
+        Boolean result = isEnabledInternal(id);
+        return result == null ? defaultValue : result;
+    }
+
     /** Returns the stored value or null if not set. */
     private Boolean isEnabledInternal(int id) {
         try {
@@ -98,6 +121,10 @@
         return null;
     }
 
+    private boolean isEnabledInOverlay(@BoolRes int resId) {
+        return mResources.getBoolean(resId);
+    }
+
     /** Set whether a given {@link BooleanFlag} is enabled or not. */
     @Override
     public void setEnabled(int id, boolean value) {
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
index 6ff175f..0934b32 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.flags;
 
-import android.content.Context;
 import android.util.SparseBooleanArray;
 
 import androidx.annotation.NonNull;
@@ -24,7 +23,6 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -41,8 +39,7 @@
 public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
     SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
     @Inject
-    public FeatureFlagManager(
-            SecureSettings secureSettings, Context context, DumpManager dumpManager) {
+    public FeatureFlagManager(DumpManager dumpManager) {
         dumpManager.registerDumpable("SysUIFlags", this);
     }
 
@@ -53,6 +50,11 @@
     public void removeListener(Listener run) {}
 
     @Override
+    public boolean isEnabled(BooleanFlag flag) {
+        return isEnabled(flag.getId(), flag.getDefault());
+    }
+
+    @Override
     public boolean isEnabled(int key, boolean defaultValue) {
         mAccessedFlags.append(key, defaultValue);
         return defaultValue;
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index a383cab..ac463eb 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -31,7 +31,6 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.ViewController;
 
@@ -49,7 +48,6 @@
     private final StatusBarStateController mStatusBarStateController;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final KeyguardBypassController mBypassController;
     private final BatteryController mBatteryController;
     private final int mDozingColor = Color.WHITE;
     private int mLockScreenColor;
@@ -71,14 +69,12 @@
             BroadcastDispatcher broadcastDispatcher,
             BatteryController batteryController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            KeyguardBypassController bypassController,
             @Main Resources resources
     ) {
         super(view);
         mStatusBarStateController = statusBarStateController;
         mBroadcastDispatcher = broadcastDispatcher;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mBypassController = bypassController;
         mBatteryController = batteryController;
 
         mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index ef3104a..2a0c285 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -27,7 +27,6 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 
 import java.util.Calendar;
 import java.util.Locale;
@@ -111,6 +110,28 @@
         super.onDetachedFromWindow();
     }
 
+    int getDozingWeight() {
+        if (useBoldedVersion()) {
+            return mDozingWeight + 100;
+        }
+        return mDozingWeight;
+    }
+
+    int getLockScreenWeight() {
+        if (useBoldedVersion()) {
+            return mLockScreenWeight + 100;
+        }
+        return mLockScreenWeight;
+    }
+
+    /**
+     * Whether to use a bolded version based on the user specified fontWeightAdjustment.
+     */
+    boolean useBoldedVersion() {
+        // "Bold text" fontWeightAdjustment is 300.
+        return getResources().getConfiguration().fontWeightAdjustment > 100;
+    }
+
     void refreshTime() {
         mTime.setTimeInMillis(System.currentTimeMillis());
         setText(DateFormat.format(mFormat, mTime));
@@ -162,7 +183,7 @@
         }
 
         setTextStyle(
-                mDozingWeight,
+                getDozingWeight(),
                 -1 /* text size, no update */,
                 mLockScreenColor,
                 false /* animate */,
@@ -171,7 +192,7 @@
                 null /* onAnimationEnd */);
 
         setTextStyle(
-                mLockScreenWeight,
+                getLockScreenWeight(),
                 -1 /* text size, no update */,
                 mLockScreenColor,
                 true, /* animate */
@@ -180,35 +201,22 @@
                 null /* onAnimationEnd */);
     }
 
-    void animateDisappear() {
-        if (mTextAnimator == null) {
-            return;
-        }
-
-        setTextStyle(
-                0 /* weight */,
-                -1 /* text size, no update */,
-                null /* color, no update */,
-                true /* animate */,
-                KeyguardBypassController.BYPASS_FADE_DURATION /* duration */,
-                0 /* delay */,
-                null /* onAnimationEnd */);
-    }
-
     void animateCharge(DozeStateGetter dozeStateGetter) {
         if (mTextAnimator == null || mTextAnimator.isRunning()) {
             // Skip charge animation if dozing animation is already playing.
             return;
         }
         Runnable startAnimPhase2 = () -> setTextStyle(
-                dozeStateGetter.isDozing() ? mDozingWeight : mLockScreenWeight/* weight */,
+                dozeStateGetter.isDozing() ? getDozingWeight() : getLockScreenWeight() /* weight */,
                 -1,
                 null,
                 true /* animate */,
                 CHARGE_ANIM_DURATION_PHASE_1,
                 0 /* delay */,
                 null /* onAnimationEnd */);
-        setTextStyle(dozeStateGetter.isDozing() ? mLockScreenWeight : mDozingWeight/* weight */,
+        setTextStyle(dozeStateGetter.isDozing()
+                        ? getLockScreenWeight()
+                        : getDozingWeight()/* weight */,
                 -1,
                 null,
                 true /* animate */,
@@ -218,7 +226,7 @@
     }
 
     void animateDoze(boolean isDozing, boolean animate) {
-        setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */,
+        setTextStyle(isDozing ? getDozingWeight() : getLockScreenWeight() /* weight */,
                 -1,
                 isDozing ? mDozingColor : mLockScreenColor,
                 animate,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 1931c0a..905495d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -167,7 +167,6 @@
                         mBroadcastDispatcher,
                         mBatteryController,
                         mKeyguardUpdateMonitor,
-                        mBypassController,
                         mResources);
         mClockViewController.init();
 
@@ -178,7 +177,6 @@
                         mBroadcastDispatcher,
                         mBatteryController,
                         mKeyguardUpdateMonitor,
-                        mBypassController,
                         mResources);
         mLargeClockViewController.init();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 85bc8f7..d27bc67 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2044,17 +2044,17 @@
     }
 
     /**
-     * @return true if there's at least one udfps enrolled
+     * @return true if there's at least one udfps enrolled for the current user.
      */
     public boolean isUdfpsEnrolled() {
         return mIsUdfpsEnrolled;
     }
 
     /**
-     * @return if udfps is available on this device. will return true even if the user hasn't
-     * enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
+     * @return true if udfps HW is supported on this device. Can return true even if the user has
+     * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
      */
-    public boolean isUdfpsAvailable() {
+    public boolean isUdfpsSupported() {
         return mAuthController.getUdfpsProps() != null
                 && !mAuthController.getUdfpsProps().isEmpty();
     }
@@ -2102,7 +2102,7 @@
         }
 
         updateUdfpsEnrolled(getCurrentUser());
-        final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsEnrolled());
+        final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
         final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
                 || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
         if (runningOrRestarting && !shouldListenForFingerprint) {
@@ -2407,7 +2407,7 @@
             } else {
                 mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
                         mFingerprintAuthenticationCallback, null /* handler */,
-                        FingerprintManager.SENSOR_ID_ANY, userId);
+                        FingerprintManager.SENSOR_ID_ANY, userId, 0 /* flags */);
             }
             setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
         }
@@ -2990,7 +2990,7 @@
 
     /**
      * Register to receive notifications about general keyguard information
-     * (see {@link InfoCallback}.
+     * (see {@link KeyguardUpdateMonitorCallback}.
      *
      * @param callback The callback to register
      */
@@ -3388,11 +3388,11 @@
                     + " expected=" + (shouldListenForFingerprint(isUdfpsEnrolled()) ? 1 : 0));
             pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
             pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
-            pw.println("    udfpsEnrolled=" + isUdfpsEnrolled());
             pw.println("    mFingerprintLockedOut=" + mFingerprintLockedOut);
             pw.println("    mFingerprintLockedOutPermanent=" + mFingerprintLockedOutPermanent);
             pw.println("    enabledByUser=" + mBiometricEnabledForUser.get(userId));
-            if (isUdfpsEnrolled()) {
+            if (isUdfpsSupported()) {
+                pw.println("        udfpsEnrolled=" + isUdfpsEnrolled());
                 pw.println("        shouldListenForUdfps=" + shouldListenForFingerprint(true));
                 pw.println("        bouncerVisible=" + mBouncer);
                 pw.println("        mStatusBarState="
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 23438a9..a382b53 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -136,6 +136,13 @@
                             .setStartDelay(delay);
                 }
                 animator.start();
+            } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
+                mKeyguardViewVisibilityAnimating = true;
+
+                // Ask the screen off animation controller to animate the keyguard visibility for us
+                // since it may need to be cancelled due to keyguard lifecycle events.
+                mUnlockedScreenOffAnimationController.animateInKeyguard(
+                        mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
             } else if (mLastOccludedState && !isOccluded) {
                 // An activity was displayed over the lock screen, and has now gone away
                 mView.setVisibility(View.VISIBLE);
@@ -147,13 +154,6 @@
                         .alpha(1f)
                         .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
                         .start();
-            } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
-                mKeyguardViewVisibilityAnimating = true;
-
-                // Ask the screen off animation controller to animate the keyguard visibility for us
-                // since it may need to be cancelled due to keyguard lifecycle events.
-                mUnlockedScreenOffAnimationController.animateInKeyguard(
-                        mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
             } else {
                 mView.setVisibility(View.VISIBLE);
                 mView.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8a0b5b8..c7be3ce 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -435,7 +435,7 @@
         boolean wasUdfpsSupported = mUdfpsSupported;
         boolean wasUdfpsEnrolled = mUdfpsEnrolled;
 
-        mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+        mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
         mView.setUseBackground(mUdfpsSupported);
 
         mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/SystemUI.java
rename to packages/SystemUI/src/com/android/systemui/CoreStartable.java
index c6a750a..0df8e02 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -30,17 +30,18 @@
 
 /**
  * A top-level module of system UI code (sometimes called "system UI services" elsewhere in code).
- * Which SystemUI modules are loaded can be controlled via a config resource.
+ * Which CoreStartable modules are loaded can be controlled via a config resource.
  *
  * @see SystemUIApplication#startServicesIfNeeded()
  */
-public abstract class SystemUI implements Dumpable {
+public abstract class CoreStartable implements Dumpable {
     protected final Context mContext;
 
-    public SystemUI(Context context) {
+    public CoreStartable(Context context) {
         mContext = context;
     }
 
+    /** Main entry point for implementations. Called shortly after app startup. */
     public abstract void start();
 
     protected void onConfigurationChanged(Configuration newConfig) {
@@ -54,6 +55,7 @@
     protected void onBootCompleted() {
     }
 
+    /** TODO(b/205725937): Move this. SystemUIApplication? */
     public static void overrideNotificationAppName(Context context, Notification.Builder n,
             boolean system) {
         final Bundle extras = new Bundle();
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 744a77f..0b967b7 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -17,6 +17,8 @@
 
 package com.android.systemui;
 
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -32,6 +34,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
@@ -544,6 +547,7 @@
         }
         if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
                     " mNaturalHeight: " + mNaturalHeight);
+        InteractionJankMonitor.getInstance().begin(v, CUJ_NOTIFICATION_SHADE_ROW_EXPAND);
         return true;
     }
 
@@ -608,6 +612,9 @@
                     }
                     mCallback.setUserLockedChild(scaledView, false);
                     mScaleAnimation.removeListener(this);
+                    if (wasClosed) {
+                        InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_EXPAND);
+                    }
                 }
 
                 @Override
@@ -625,6 +632,9 @@
             mCallback.setUserExpandedChild(mResizedView, nowExpanded);
             mCallback.setUserLockedChild(mResizedView, false);
             mScaler.setView(null);
+            if (wasClosed) {
+                InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_EXPAND);
+            }
         }
 
         mExpanding = false;
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index d325b92..bc2a1ff 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -35,7 +35,7 @@
  * system that are used for testing the latency.
  */
 @SysUISingleton
-public class LatencyTester extends SystemUI {
+public class LatencyTester extends CoreStartable {
 
     private static final String
             ACTION_FINGERPRINT_WAKE =
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index b2fae9d..e84024d 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -107,7 +107,7 @@
  * for antialiasing and emulation purposes.
  */
 @SysUISingleton
-public class ScreenDecorations extends SystemUI implements Tunable {
+public class ScreenDecorations extends CoreStartable implements Tunable {
     private static final boolean DEBUG = false;
     private static final String TAG = "ScreenDecorations";
 
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index 0b997d0..d7da63b 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -38,7 +38,7 @@
  * @see SliceBroadcastRelay
  */
 @SysUISingleton
-public class SliceBroadcastRelayHandler extends SystemUI {
+public class SliceBroadcastRelayHandler extends CoreStartable {
     private static final String TAG = "SliceBroadcastRelay";
     private static final boolean DEBUG = false;
 
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 3555e8d..4a0c30c 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -372,6 +372,11 @@
     }
 
     /**
+     * After dismissChild() and related animation finished, this function will be called.
+     */
+    protected void onDismissChildWithAnimationFinished() {}
+
+    /**
      * @param view The view to be dismissed
      * @param velocity The desired pixels/second speed at which the view should move
      * @param useAccelerateInterpolator Should an accelerating Interpolator be used
@@ -436,6 +441,7 @@
 
         Animator anim = getViewTranslationAnimator(animView, newPos, updateListener);
         if (anim == null) {
+            onDismissChildWithAnimationFinished();
             return;
         }
         if (useAccelerateInterpolator) {
@@ -481,6 +487,7 @@
                 if (!mDisableHwLayers) {
                     animView.setLayerType(View.LAYER_TYPE_NONE, null);
                 }
+                onDismissChildWithAnimationFinished();
             }
         });
 
@@ -505,6 +512,11 @@
         // Do nothing
     }
 
+    /**
+     * After snapChild() and related animation finished, this function will be called.
+     */
+    protected void onSnapChildWithAnimationFinished() {}
+
     public void snapChild(final View animView, final float targetLeft, float velocity) {
         final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
         AnimatorUpdateListener updateListener = animation -> onTranslationUpdate(animView,
@@ -512,6 +524,7 @@
 
         Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
         if (anim == null) {
+            onSnapChildWithAnimationFinished();
             return;
         }
         anim.addListener(new AnimatorListenerAdapter() {
@@ -529,6 +542,7 @@
                     updateSwipeProgressFromOffset(animView, canBeDismissed);
                     resetSwipeState();
                 }
+                onSnapChildWithAnimationFinished();
             }
         });
         prepareSnapBackAnimation(animView, anim);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 4fd2701..61d8c25 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -60,7 +60,7 @@
     /**
      * Hold a reference on the stuff we start.
      */
-    private SystemUI[] mServices;
+    private CoreStartable[] mServices;
     private boolean mServicesStarted;
     private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
     private GlobalRootComponent mRootComponent;
@@ -190,7 +190,7 @@
         if (mServicesStarted) {
             return;
         }
-        mServices = new SystemUI[services.length];
+        mServices = new CoreStartable[services.length];
 
         if (!mBootCompleteCache.isBootComplete()) {
             // check to see if maybe it was already completed long before we began
@@ -217,10 +217,10 @@
             log.traceBegin(metricsPrefix + clsName);
             long ti = System.currentTimeMillis();
             try {
-                SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
+                CoreStartable obj = mComponentHelper.resolveCoreStartable(clsName);
                 if (obj == null) {
                     Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
-                    obj = (SystemUI) constructor.newInstance(this);
+                    obj = (CoreStartable) constructor.newInstance(this);
                 }
                 mServices[i] = obj;
             } catch (ClassNotFoundException
@@ -265,7 +265,7 @@
         }
     }
 
-    public SystemUI[] getServices() {
+    public CoreStartable[] getServices() {
         return mServices;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java
index 13d847b..139448c0 100644
--- a/packages/SystemUI/src/com/android/systemui/VendorServices.java
+++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java
@@ -21,7 +21,7 @@
 /**
  * Placeholder for any vendor-specific services.
  */
-public class VendorServices extends SystemUI {
+public class VendorServices extends CoreStartable {
 
     public VendorServices(Context context) {
         super(context);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 294d1f4..20d6e32 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -49,7 +49,7 @@
 import com.android.internal.R;
 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
 import com.android.internal.util.ScreenshotHelper;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.statusbar.CommandQueue;
@@ -69,7 +69,7 @@
  * Class to register system actions with accessibility framework.
  */
 @SysUISingleton
-public class SystemActions extends SystemUI {
+public class SystemActions extends CoreStartable {
     private static final String TAG = "SystemActions";
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index d34ac71..33ce206 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -35,7 +35,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
@@ -54,7 +54,7 @@
  * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
  */
 @SysUISingleton
-public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
+public class WindowMagnification extends CoreStartable implements WindowMagnifierCallback,
         CommandQueue.Callbacks {
     private static final String TAG = "WindowMagnification";
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 36fef3e..8cb608f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -106,6 +106,15 @@
     void enableWindowMagnification(float scale, float centerX, float centerY,
             @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
         sendAnimationCallback(false);
+        // Enable window magnification without animation immediately.
+        if (animationCallback == null) {
+            if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
+                mValueAnimator.cancel();
+            }
+            mController.enableWindowMagnification(scale, centerX, centerY);
+            setState(STATE_ENABLED);
+            return;
+        }
         mAnimationCallback = animationCallback;
         setupEnableAnimationSpecs(scale, centerX, centerY);
         if (mEndSpec.equals(mStartSpec)) {
@@ -173,6 +182,16 @@
     void deleteWindowMagnification(
             @Nullable IRemoteMagnificationAnimationCallback animationCallback) {
         sendAnimationCallback(false);
+        // Delete window magnification without animation.
+        if (animationCallback == null) {
+            if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
+                mValueAnimator.cancel();
+            }
+            mController.deleteWindowMagnification();
+            setState(STATE_DISABLED);
+            return;
+        }
+
         mAnimationCallback = animationCallback;
         if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
             if (mState == STATE_DISABLED) {
@@ -223,8 +242,7 @@
         if (mEndAnimationCanceled) {
             return;
         }
-        if (isReverse) {
-            mController.deleteWindowMagnification();
+        if (Float.isNaN(mController.getScale())) {
             setState(STATE_DISABLED);
         } else {
             setState(STATE_ENABLED);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0893e89..b48def2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -262,6 +262,9 @@
      * Deletes the magnification window.
      */
     void deleteWindowMagnification() {
+        if (!isWindowVisible()) {
+            return;
+        }
         if (mMirrorSurface != null) {
             mTransaction.remove(mMirrorSurface).apply();
             mMirrorSurface = null;
@@ -690,7 +693,10 @@
     }
 
     /**
-     * Enables window magnification with specified parameters.
+     * Enables window magnification with specified parameters. If the given scale is <strong>less
+     * than or equal to 1.0f<strong>, then
+     * {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to
+     * be consistent with the behavior of display magnification.
      *
      * @param scale   the target scale, or {@link Float#NaN} to leave unchanged
      * @param centerX the screen-relative X coordinate around which to center,
@@ -699,6 +705,11 @@
      *                or {@link Float#NaN} to leave unchanged.
      */
     void enableWindowMagnification(float scale, float centerX, float centerY) {
+        if (Float.compare(scale, 1.0f)  <= 0) {
+            deleteWindowMagnification();
+            return;
+        }
+
         final float offsetX = Float.isNaN(centerX) ? 0
                 : centerX - mMagnificationFrame.exactCenterX();
         final float offsetY = Float.isNaN(centerY) ? 0
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
index 376368f..d80d9cc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
@@ -21,12 +21,19 @@
 import android.content.Context;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
 
 /**
  * Manages the layout for under-display fingerprint sensors (UDFPS). Ensures that UI elements
  * do not overlap with
  */
 public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
+    private static final String TAG = "AuthBiometricUdfpsView";
+
     @Nullable private UdfpsDialogMeasureAdapter mMeasureAdapter;
 
     public AuthBiometricUdfpsView(Context context) {
@@ -51,4 +58,23 @@
                 ? mMeasureAdapter.onMeasureInternal(width, height, layoutParams)
                 : layoutParams;
     }
+
+    @Override
+    void onLayoutInternal() {
+        super.onLayoutInternal();
+
+        // Move the UDFPS icon and indicator text if necessary. This probably only needs to happen
+        // for devices where the UDFPS sensor is too low.
+        // TODO(b/201510778): Update this logic to support cases where the sensor or text overlap
+        //  the button bar area.
+        final int bottomSpacerHeight = mMeasureAdapter.getBottomSpacerHeight();
+        Log.w(TAG, "bottomSpacerHeight: " + bottomSpacerHeight);
+        if (bottomSpacerHeight < 0) {
+            FrameLayout iconFrame = findViewById(R.id.biometric_icon_frame);
+            iconFrame.setTranslationY(-bottomSpacerHeight);
+
+            TextView indicator = findViewById(R.id.indicator);
+            indicator.setTranslationY(-bottomSpacerHeight);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 7215736..29e5574 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -58,7 +58,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.assist.ui.DisplayUtils;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -84,7 +84,7 @@
  * {@link com.android.keyguard.KeyguardUpdateMonitor}
  */
 @SysUISingleton
-public class AuthController extends SystemUI implements CommandQueue.Callbacks,
+public class AuthController extends CoreStartable implements CommandQueue.Callbacks,
         AuthDialogCallback, DozeReceiver {
 
     private static final String TAG = "AuthController";
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index ec17d4e..90a1e5e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.biometrics
 
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.PointF
@@ -29,7 +31,9 @@
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.commandline.Command
@@ -41,13 +45,10 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.ViewController
+import com.android.systemui.util.leak.RotationUtils
 import java.io.PrintWriter
 import javax.inject.Inject
 import javax.inject.Provider
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.util.leak.RotationUtils
-
-private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L
 
 /***
  * Controls the ripple effect that shows when authentication is successful.
@@ -141,11 +142,12 @@
 
     private fun showUnlockedRipple() {
         notificationShadeWindowController.setForcePluginOpen(true, this)
-        val useCircleReveal = circleReveal != null && biometricUnlockController.isWakeAndUnlock
         val lightRevealScrim = statusBar.lightRevealScrim
-        if (useCircleReveal) {
-            lightRevealScrim?.revealEffect = circleReveal!!
-            startLightRevealScrimOnKeyguardFadingAway = true
+        if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+            circleReveal?.let {
+                lightRevealScrim?.revealEffect = it
+                startLightRevealScrimOnKeyguardFadingAway = true
+            }
         }
 
         mView.startUnlockedRipple(
@@ -160,19 +162,29 @@
         if (keyguardStateController.isKeyguardFadingAway) {
             val lightRevealScrim = statusBar.lightRevealScrim
             if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
-                val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
+                ValueAnimator.ofFloat(.1f, 1f).apply {
                     interpolator = Interpolators.LINEAR_OUT_SLOW_IN
                     duration = RIPPLE_ANIMATION_DURATION
                     startDelay = keyguardStateController.keyguardFadingAwayDelay
                     addUpdateListener { animator ->
                         if (lightRevealScrim.revealEffect != circleReveal) {
-                            // if the something else took over the reveal, let's do nothing.
+                            // if something else took over the reveal, let's do nothing.
                             return@addUpdateListener
                         }
                         lightRevealScrim.revealAmount = animator.animatedValue as Float
                     }
+                    addListener(object : AnimatorListenerAdapter() {
+                        override fun onAnimationEnd(animation: Animator?) {
+                            // Reset light reveal scrim to the default, so the StatusBar
+                            // can handle any subsequent light reveal changes
+                            // (ie: from dozing changes)
+                            if (lightRevealScrim.revealEffect == circleReveal) {
+                                lightRevealScrim.revealEffect = LiftReveal
+                            }
+                        }
+                    })
+                    start()
                 }
-                revealAnimator.start()
                 startLightRevealScrimOnKeyguardFadingAway = false
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 6cc8acf..6afe420 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.hardware.biometrics.SensorLocationInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.os.Build;
 import android.util.Log;
 import android.view.Surface;
 import android.view.View;
@@ -30,6 +31,7 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowMetrics;
 import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -41,16 +43,18 @@
  */
 public class UdfpsDialogMeasureAdapter {
     private static final String TAG = "UdfpsDialogMeasurementAdapter";
+    private static final boolean DEBUG = Build.IS_USERDEBUG || Build.IS_ENG;
 
     @NonNull private final ViewGroup mView;
     @NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
-
     @Nullable private WindowManager mWindowManager;
+    private int mBottomSpacerHeight;
 
     public UdfpsDialogMeasureAdapter(
             @NonNull ViewGroup view, @NonNull FingerprintSensorPropertiesInternal sensorProps) {
         mView = view;
         mSensorProps = sensorProps;
+        mWindowManager = mView.getContext().getSystemService(WindowManager.class);
     }
 
     @NonNull
@@ -75,19 +79,27 @@
         }
     }
 
+    /**
+     * @return the actual (and possibly negative) bottom spacer height. If negative, this indicates
+     * that the UDFPS sensor is too low. Our current xml and custom measurement logic is very hard
+     * too cleanly support this case. So, let's have the onLayout code translate the sensor location
+     * instead.
+     */
+    int getBottomSpacerHeight() {
+        return mBottomSpacerHeight;
+    }
+
     @NonNull
     private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height) {
-        // Get the height of the everything below the icon. Currently, that's the indicator and
-        // button bar.
-        final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
-        final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
+        final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
 
         // Figure out where the bottom of the sensor anim should be.
-        // Navbar + dialogMargin + buttonBar + textIndicator + spacerHeight = sensorDistFromBottom
+        final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
+        final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
         final int dialogMargin = getDialogMarginPx();
-        final int displayHeight = getWindowBounds().height();
-        final Insets navbarInsets = getNavbarInsets();
-        final int bottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
+        final int displayHeight = getMaximumWindowBounds(windowMetrics).height();
+        final Insets navbarInsets = getNavbarInsets(windowMetrics);
+        mBottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
                 mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight,
                 dialogMargin, navbarInsets.bottom);
 
@@ -123,9 +135,10 @@
                                 MeasureSpec.EXACTLY));
             } else if (child.getId() == R.id.space_below_icon) {
                 // Set the spacer height so the fingerprint icon is on the physical sensor area
+                final int clampedSpacerHeight = Math.max(mBottomSpacerHeight, 0);
                 child.measure(
                         MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY));
+                        MeasureSpec.makeMeasureSpec(clampedSpacerHeight, MeasureSpec.EXACTLY));
             } else {
                 child.measure(
                         MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -142,6 +155,8 @@
 
     @NonNull
     private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height) {
+        final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
+
         // Find the spacer height needed to vertically align the icon with the sensor.
         final int titleHeight = getViewHeightPx(R.id.title);
         final int subtitleHeight = getViewHeightPx(R.id.subtitle);
@@ -149,13 +164,14 @@
         final int topSpacerHeight = getViewHeightPx(R.id.space_above_icon);
         final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
         final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
-        final Insets navbarInsets = getNavbarInsets();
+
+        final Insets navbarInsets = getNavbarInsets(windowMetrics);
         final int bottomSpacerHeight = calculateBottomSpacerHeightForLandscape(titleHeight,
                 subtitleHeight, descriptionHeight, topSpacerHeight, textIndicatorHeight,
                 buttonBarHeight, navbarInsets.bottom);
 
         // Find the spacer width needed to horizontally align the icon with the sensor.
-        final int displayWidth = getWindowBounds().width();
+        final int displayWidth = getMaximumWindowBounds(windowMetrics).width();
         final int dialogMargin = getDialogMarginPx();
         final int horizontalInset = navbarInsets.left + navbarInsets.right;
         final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
@@ -225,28 +241,15 @@
     }
 
     @NonNull
-    private Insets getNavbarInsets() {
-        final WindowManager windowManager = getWindowManager();
-        return windowManager != null && windowManager.getCurrentWindowMetrics() != null
-                ? windowManager.getCurrentWindowMetrics().getWindowInsets()
-                .getInsets(WindowInsets.Type.navigationBars())
+    private static Insets getNavbarInsets(@Nullable WindowMetrics windowMetrics) {
+        return windowMetrics != null
+                ? windowMetrics.getWindowInsets().getInsets(WindowInsets.Type.navigationBars())
                 : Insets.NONE;
     }
 
     @NonNull
-    private Rect getWindowBounds() {
-        final WindowManager windowManager = getWindowManager();
-        return windowManager != null && windowManager.getCurrentWindowMetrics() != null
-                ? windowManager.getCurrentWindowMetrics().getBounds()
-                : new Rect();
-    }
-
-    @Nullable
-    private WindowManager getWindowManager() {
-        if (mWindowManager == null) {
-            mWindowManager = mView.getContext().getSystemService(WindowManager.class);
-        }
-        return mWindowManager;
+    private static Rect getMaximumWindowBounds(@Nullable WindowMetrics windowMetrics) {
+        return windowMetrics != null ? windowMetrics.getBounds() : new Rect();
     }
 
     /**
@@ -269,11 +272,13 @@
                 - dialogMarginPx
                 - navbarBottomInsetPx;
 
-        Log.d(TAG, "Display height: " + displayHeightPx
-                + ", Distance from bottom: " + sensorDistanceFromBottom
-                + ", Bottom margin: " + dialogMarginPx
-                + ", Navbar bottom inset: " + navbarBottomInsetPx
-                + ", Bottom spacer height (portrait): " + spacerHeight);
+        if (DEBUG) {
+            Log.d(TAG, "Display height: " + displayHeightPx
+                    + ", Distance from bottom: " + sensorDistanceFromBottom
+                    + ", Bottom margin: " + dialogMarginPx
+                    + ", Navbar bottom inset: " + navbarBottomInsetPx
+                    + ", Bottom spacer height (portrait): " + spacerHeight);
+        }
 
         return spacerHeight;
     }
@@ -298,14 +303,16 @@
                 - dialogHeightBelowIcon
                 - navbarBottomInsetPx;
 
-        Log.d(TAG, "Title height: " + titleHeightPx
-                + ", Subtitle height: " + subtitleHeightPx
-                + ", Description height: " + descriptionHeightPx
-                + ", Top spacer height: " + topSpacerHeightPx
-                + ", Text indicator height: " + textIndicatorHeightPx
-                + ", Button bar height: " + buttonBarHeightPx
-                + ", Navbar bottom inset: " + navbarBottomInsetPx
-                + ", Bottom spacer height (landscape): " + bottomSpacerHeight);
+        if (DEBUG) {
+            Log.d(TAG, "Title height: " + titleHeightPx
+                    + ", Subtitle height: " + subtitleHeightPx
+                    + ", Description height: " + descriptionHeightPx
+                    + ", Top spacer height: " + topSpacerHeightPx
+                    + ", Text indicator height: " + textIndicatorHeightPx
+                    + ", Button bar height: " + buttonBarHeightPx
+                    + ", Navbar bottom inset: " + navbarBottomInsetPx
+                    + ", Bottom spacer height (landscape): " + bottomSpacerHeight);
+        }
 
         return bottomSpacerHeight;
     }
@@ -328,11 +335,13 @@
                 - dialogMarginPx
                 - navbarHorizontalInsetPx;
 
-        Log.d(TAG, "Display width: " + displayWidthPx
-                + ", Distance from edge: " + sensorDistanceFromEdge
-                + ", Dialog margin: " + dialogMarginPx
-                + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
-                + ", Horizontal spacer width (landscape): " + horizontalPadding);
+        if (DEBUG) {
+            Log.d(TAG, "Display width: " + displayWidthPx
+                    + ", Distance from edge: " + sensorDistanceFromEdge
+                    + ", Dialog margin: " + dialogMarginPx
+                    + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
+                    + ", Horizontal spacer width (landscape): " + horizontalPadding);
+        }
 
         return horizontalPadding;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
index 1e7449c..f53221c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
@@ -20,7 +20,7 @@
 import android.app.Service;
 import android.content.BroadcastReceiver;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.recents.RecentsImplementation;
 
 /**
@@ -37,7 +37,7 @@
     Service resolveService(String className);
 
     /** Turns a classname into an instance of the class or returns null. */
-    SystemUI resolveSystemUI(String className);
+    CoreStartable resolveCoreStartable(String className);
 
     /** Turns a classname into an instance of the class or returns null. */
     BroadcastReceiver resolveBroadcastReceiver(String className);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
index b41915b..fba8d35 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
@@ -20,7 +20,7 @@
 import android.app.Service;
 import android.content.BroadcastReceiver;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.recents.RecentsImplementation;
 
 import java.util.Map;
@@ -35,14 +35,14 @@
 public class ContextComponentResolver implements ContextComponentHelper {
     private final Map<Class<?>, Provider<Activity>> mActivityCreators;
     private final Map<Class<?>, Provider<Service>> mServiceCreators;
-    private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
+    private final Map<Class<?>, Provider<CoreStartable>> mSystemUICreators;
     private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
     private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;
 
     @Inject
     ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
             Map<Class<?>, Provider<Service>> serviceCreators,
-            Map<Class<?>, Provider<SystemUI>> systemUICreators,
+            Map<Class<?>, Provider<CoreStartable>> systemUICreators,
             Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
             Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
         mActivityCreators = activityCreators;
@@ -88,7 +88,7 @@
      * Looks up the SystemUI class name to see if Dagger has an instance of it.
      */
     @Override
-    public SystemUI resolveSystemUI(String className) {
+    public CoreStartable resolveCoreStartable(String className) {
         return resolve(className, mSystemUICreators);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index a5d4d80..e5c6ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -16,10 +16,10 @@
 
 package com.android.systemui.dagger;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.LatencyTester;
 import com.android.systemui.ScreenDecorations;
 import com.android.systemui.SliceBroadcastRelayHandler;
-import com.android.systemui.SystemUI;
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.accessibility.WindowMagnification;
 import com.android.systemui.biometrics.AuthController;
@@ -63,145 +63,145 @@
     @Binds
     @IntoMap
     @ClassKey(AuthController.class)
-    public abstract SystemUI bindAuthController(AuthController service);
+    public abstract CoreStartable bindAuthController(AuthController service);
 
     /** Inject into GarbageMonitor.Service. */
     @Binds
     @IntoMap
     @ClassKey(GarbageMonitor.Service.class)
-    public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service sysui);
+    public abstract CoreStartable bindGarbageMonitorService(GarbageMonitor.Service sysui);
 
     /** Inject into GlobalActionsComponent. */
     @Binds
     @IntoMap
     @ClassKey(GlobalActionsComponent.class)
-    public abstract SystemUI bindGlobalActionsComponent(GlobalActionsComponent sysui);
+    public abstract CoreStartable bindGlobalActionsComponent(GlobalActionsComponent sysui);
 
     /** Inject into InstantAppNotifier. */
     @Binds
     @IntoMap
     @ClassKey(InstantAppNotifier.class)
-    public abstract SystemUI bindInstantAppNotifier(InstantAppNotifier sysui);
+    public abstract CoreStartable bindInstantAppNotifier(InstantAppNotifier sysui);
 
     /** Inject into KeyguardViewMediator. */
     @Binds
     @IntoMap
     @ClassKey(KeyguardViewMediator.class)
-    public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+    public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui);
 
     /** Inject into LatencyTests. */
     @Binds
     @IntoMap
     @ClassKey(LatencyTester.class)
-    public abstract SystemUI bindLatencyTester(LatencyTester sysui);
+    public abstract CoreStartable bindLatencyTester(LatencyTester sysui);
 
     /** Inject into PowerUI. */
     @Binds
     @IntoMap
     @ClassKey(PowerUI.class)
-    public abstract SystemUI bindPowerUI(PowerUI sysui);
+    public abstract CoreStartable bindPowerUI(PowerUI sysui);
 
     /** Inject into Recents. */
     @Binds
     @IntoMap
     @ClassKey(Recents.class)
-    public abstract SystemUI bindRecents(Recents sysui);
+    public abstract CoreStartable bindRecents(Recents sysui);
 
     /** Inject into ScreenDecorations. */
     @Binds
     @IntoMap
     @ClassKey(ScreenDecorations.class)
-    public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+    public abstract CoreStartable bindScreenDecorations(ScreenDecorations sysui);
 
     /** Inject into ShortcutKeyDispatcher. */
     @Binds
     @IntoMap
     @ClassKey(ShortcutKeyDispatcher.class)
-    public abstract SystemUI bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
+    public abstract CoreStartable bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
 
     /** Inject into SliceBroadcastRelayHandler. */
     @Binds
     @IntoMap
     @ClassKey(SliceBroadcastRelayHandler.class)
-    public abstract SystemUI bindSliceBroadcastRelayHandler(SliceBroadcastRelayHandler sysui);
+    public abstract CoreStartable bindSliceBroadcastRelayHandler(SliceBroadcastRelayHandler sysui);
 
     /** Inject into StatusBar. */
     @Binds
     @IntoMap
     @ClassKey(StatusBar.class)
-    public abstract SystemUI bindsStatusBar(StatusBar sysui);
+    public abstract CoreStartable bindsStatusBar(StatusBar sysui);
 
     /** Inject into SystemActions. */
     @Binds
     @IntoMap
     @ClassKey(SystemActions.class)
-    public abstract SystemUI bindSystemActions(SystemActions sysui);
+    public abstract CoreStartable bindSystemActions(SystemActions sysui);
 
     /** Inject into ThemeOverlayController. */
     @Binds
     @IntoMap
     @ClassKey(ThemeOverlayController.class)
-    public abstract SystemUI bindThemeOverlayController(ThemeOverlayController sysui);
+    public abstract CoreStartable bindThemeOverlayController(ThemeOverlayController sysui);
 
     /** Inject into ToastUI. */
     @Binds
     @IntoMap
     @ClassKey(ToastUI.class)
-    public abstract SystemUI bindToastUI(ToastUI service);
+    public abstract CoreStartable bindToastUI(ToastUI service);
 
     /** Inject into TvStatusBar. */
     @Binds
     @IntoMap
     @ClassKey(TvStatusBar.class)
-    public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
+    public abstract CoreStartable bindsTvStatusBar(TvStatusBar sysui);
 
     /** Inject into TvNotificationPanel. */
     @Binds
     @IntoMap
     @ClassKey(TvNotificationPanel.class)
-    public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui);
+    public abstract CoreStartable bindsTvNotificationPanel(TvNotificationPanel sysui);
 
     /** Inject into TvOngoingPrivacyChip. */
     @Binds
     @IntoMap
     @ClassKey(TvOngoingPrivacyChip.class)
-    public abstract SystemUI bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui);
+    public abstract CoreStartable bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui);
 
     /** Inject into VolumeUI. */
     @Binds
     @IntoMap
     @ClassKey(VolumeUI.class)
-    public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+    public abstract CoreStartable bindVolumeUI(VolumeUI sysui);
 
     /** Inject into WindowMagnification. */
     @Binds
     @IntoMap
     @ClassKey(WindowMagnification.class)
-    public abstract SystemUI bindWindowMagnification(WindowMagnification sysui);
+    public abstract CoreStartable bindWindowMagnification(WindowMagnification sysui);
 
     /** Inject into WMShell. */
     @Binds
     @IntoMap
     @ClassKey(WMShell.class)
-    public abstract SystemUI bindWMShell(WMShell sysui);
+    public abstract CoreStartable bindWMShell(WMShell sysui);
 
     /** Inject into HomeSoundEffectController. */
     @Binds
     @IntoMap
     @ClassKey(HomeSoundEffectController.class)
-    public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui);
+    public abstract CoreStartable bindHomeSoundEffectController(HomeSoundEffectController sysui);
 
     /** Inject into DreamOverlay. */
     @Binds
     @IntoMap
     @ClassKey(DreamOverlayRegistrant.class)
-    public abstract SystemUI bindDreamOverlayRegistrant(
+    public abstract CoreStartable bindDreamOverlayRegistrant(
             DreamOverlayRegistrant dreamOverlayRegistrant);
 
     /** Inject into AppWidgetOverlayPrimer. */
     @Binds
     @IntoMap
     @ClassKey(AppWidgetOverlayPrimer.class)
-    public abstract SystemUI bindAppWidgetOverlayPrimer(
+    public abstract CoreStartable bindAppWidgetOverlayPrimer(
             AppWidgetOverlayPrimer appWidgetOverlayPrimer);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 20c46da..994c630 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -30,8 +30,8 @@
 import android.service.dreams.IDreamManager;
 import android.util.Log;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.qualifiers.Main;
 
 import javax.inject.Inject;
@@ -40,7 +40,7 @@
  * {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
  * the designated dream overlay component.
  */
-public class DreamOverlayRegistrant extends SystemUI {
+public class DreamOverlayRegistrant extends CoreStartable {
     private static final String TAG = "DreamOverlayRegistrant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private final IDreamManager mDreamManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
index a0c7c29..563f707 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
@@ -23,8 +23,8 @@
 
 import androidx.constraintlayout.widget.ConstraintSet;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.OverlayHostView;
@@ -36,7 +36,7 @@
  * {@link AppWidgetOverlayPrimer} reads the configured App Widget Overlay from resources on start
  * and populates them into the {@link DreamOverlayStateController}.
  */
-public class AppWidgetOverlayPrimer extends SystemUI {
+public class AppWidgetOverlayPrimer extends CoreStartable {
     private final Resources mResources;
     private final DreamOverlayStateController mDreamOverlayStateController;
     private final AppWidgetOverlayComponent.Factory mComponentFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 6880674..5f7ad58 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -17,22 +17,11 @@
 package com.android.systemui.flags;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.util.FeatureFlagUtils;
 import android.util.Log;
-import android.util.SparseArray;
 import android.widget.Toast;
 
-import androidx.annotation.BoolRes;
-
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 import javax.inject.Inject;
 
@@ -43,31 +32,13 @@
  */
 @SysUISingleton
 public class FeatureFlags {
-    private final Resources mResources;
     private final FlagReader mFlagReader;
     private final Context mContext;
-    private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
-    private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
-    private final SparseArray<Boolean> mCachedFlags = new SparseArray<>();
 
     @Inject
-    public FeatureFlags(@Main Resources resources, FlagReader flagReader, Context context) {
-        mResources = resources;
+    public FeatureFlags(FlagReader flagReader, Context context) {
         mFlagReader = flagReader;
         mContext = context;
-
-        flagReader.addListener(mListener);
-    }
-
-    private final FlagReader.Listener mListener = id -> {
-        if (mListeners.containsKey(id) && mFlagMap.containsKey(id)) {
-            mListeners.get(id).forEach(listener -> listener.onFlagChanged(mFlagMap.get(id)));
-        }
-    };
-
-    @VisibleForTesting
-    void addFlag(Flag<?> flag) {
-        mFlagMap.put(flag.getId(), flag);
     }
 
     /**
@@ -75,32 +46,7 @@
      * @return The value of the flag.
      */
     public boolean isEnabled(BooleanFlag flag) {
-        boolean def = flag.getDefault();
-        if (flag.hasResourceOverride()) {
-            try {
-                def = isEnabledInOverlay(flag.getResourceOverride());
-            } catch (Resources.NotFoundException e) {
-                // no-op
-            }
-        }
-        return mFlagReader.isEnabled(flag.getId(), def);
-    }
-
-    /**
-     * @param flag The {@link IntFlag} of interest.
-
-    /** Add a listener for a specific flag. */
-    public void addFlagListener(Flag<?> flag, Listener listener) {
-        mListeners.putIfAbsent(flag.getId(), new ArrayList<>());
-        mListeners.get(flag.getId()).add(listener);
-        mFlagMap.putIfAbsent(flag.getId(), flag);
-    }
-
-    /** Remove a listener for a specific flag. */
-    public void removeFlagListener(Flag<?> flag, Listener listener) {
-        if (mListeners.containsKey(flag.getId())) {
-            mListeners.get(flag.getId()).remove(listener);
-        }
+        return mFlagReader.isEnabled(flag);
     }
 
     public void assertLegacyPipelineEnabled() {
@@ -119,10 +65,6 @@
         return false;
     }
 
-    public boolean isNewNotifPipelineEnabled() {
-        return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE);
-    }
-
     public boolean isNewNotifPipelineRenderingEnabled() {
         return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING);
     }
@@ -209,20 +151,4 @@
     public static boolean isProviderModelSettingEnabled(Context context) {
         return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
     }
-
-    private boolean isEnabledInOverlay(@BoolRes int resId) {
-        synchronized (mCachedFlags) {
-            if (!mCachedFlags.contains(resId)) {
-                mCachedFlags.put(resId, mResources.getBoolean(resId));
-            }
-
-            return mCachedFlags.get(resId);
-        }
-    }
-
-    /** Simple interface for beinga alerted when a specific flag changes value. */
-    public interface Listener {
-        /** */
-        void onFlagChanged(Flag<?> flag);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 5be1cdf..ee24f45 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -40,9 +40,6 @@
 
     /***************************************/
     // 100 - notification
-    public static final BooleanFlag NEW_NOTIFICATION_PIPELINE =
-            new BooleanFlag(100, true);
-
     public static final BooleanFlag NEW_NOTIFICATION_PIPELINE_RENDERING =
             new BooleanFlag(101, false);
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 86c8565..e746caf 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -19,7 +19,7 @@
 import android.os.ServiceManager;
 
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -36,7 +36,8 @@
  * Manages power menu plugins and communicates power menu actions to the StatusBar.
  */
 @SysUISingleton
-public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
+public class GlobalActionsComponent extends CoreStartable
+        implements Callbacks, GlobalActionsManager {
 
     private final CommandQueue mCommandQueue;
     private final ExtensionController mExtensionController;
diff --git a/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java b/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java
deleted file mode 100644
index fba1067..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 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.idle;
-
-import android.content.Context;
-import android.service.dreams.Sandman;
-
-import javax.inject.Inject;
-
-/**
- * A helper class to the idle mode for requests related to the
- * {@link DreamService}.
- */
-public class DreamHelper {
-    @Inject
-    protected DreamHelper() {
-    }
-
-    /**
-     * Requests the system to start dreaming.
-     *
-     * @param context The context within which the dream request is sent.
-     */
-    public void startDreaming(Context context) {
-        Sandman.startDreamByUserRequest(context);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
index 6b212b7..624d01f 100644
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
@@ -24,22 +24,17 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
-import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.util.Log;
-import android.view.Choreographer;
 import android.view.View;
 
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ViewController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -52,13 +47,12 @@
  * {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen.
  */
 public class IdleHostViewController extends ViewController<IdleHostView> {
-    private static final String INPUT_MONITOR_IDENTIFIER = "IdleHostViewController";
     private static final String TAG = "IdleHostViewController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     @Retention(RetentionPolicy.RUNTIME)
     @IntDef({STATE_IDLE_MODE_ENABLED, STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_DREAMING,
-            STATE_LOW_LIGHT, STATE_IDLING, STATE_SHOULD_START_IDLING})
+            STATE_LOW_LIGHT})
     public @interface State {}
 
     // Set at construction to indicate idle mode is available.
@@ -76,76 +70,27 @@
     // Set when the device is in a low light environment.
     private static final int STATE_LOW_LIGHT = 1 << 4;
 
-    // Set when the device is idling, which is either dozing or dreaming.
-    private static final int STATE_IDLING = 1 << 5;
-
-    // Set when the controller decides that the device should start idling (either dozing or
-    // dreaming).
-    private static final int STATE_SHOULD_START_IDLING = 1 << 6;
-
     // The aggregate current state.
     private int mState;
-    private boolean mIdleModeActive;
-    private boolean mLowLightModeActive;
     private boolean mIsMonitoringLowLight;
     private boolean mIsMonitoringDream;
 
-    // Whether in a state waiting for dozing to complete before starting dreaming.
-    private boolean mDozeToDreamLock = false;
-
-    private final Context mContext;
-
-    // Timeout to idle in milliseconds.
-    private final int mIdleTimeout;
-
-    // Factory for generating input listeners.
-    private final InputMonitorFactory mInputMonitorFactory;
-
-    // Delayable executor.
-    private final DelayableExecutor mDelayableExecutor;
-
     private final BroadcastDispatcher mBroadcastDispatcher;
 
     private final PowerManager mPowerManager;
 
-    // Runnable for canceling enabling idle.
-    private Runnable mCancelEnableIdling;
-
     // Keyguard state controller for monitoring keyguard show state.
     private final KeyguardStateController mKeyguardStateController;
 
     // Status bar state controller for monitoring when the device is dozing.
     private final StatusBarStateController mStatusBarStateController;
 
-    // Looper to use for monitoring input.
-    private final Looper mLooper;
-
-    // Choreographer to use for monitoring input.
-    private final Choreographer mChoreographer;
-
-    // Helper class for DreamService related requests.
-    private final DreamHelper mDreamHelper;
-
-    // Monitor for tracking touches for activity.
-    private InputMonitorCompat mInputMonitor;
-
-    // Input receiver of touch activities.
-    private InputChannelCompat.InputEventReceiver mInputEventReceiver;
-
     // Intent filter for receiving dream broadcasts.
     private IntentFilter mDreamIntentFilter;
 
     // Monitor for the current ambient light mode. Used to trigger / exit low-light mode.
     private final AmbientLightModeMonitor mAmbientLightModeMonitor;
 
-    // Delayed callback for starting idling.
-    private final Runnable mEnableIdlingCallback = () -> {
-        if (DEBUG) {
-            Log.d(TAG, "time out, should start idling");
-        }
-        setState(STATE_SHOULD_START_IDLING, true);
-    };
-
     private final KeyguardStateController.Callback mKeyguardCallback =
             new KeyguardStateController.Callback() {
                 @Override
@@ -202,29 +147,20 @@
 
     @Inject
     protected IdleHostViewController(
-            Context context,
             BroadcastDispatcher broadcastDispatcher,
             PowerManager powerManager,
-            IdleHostView view, InputMonitorFactory factory,
-            @Main DelayableExecutor delayableExecutor,
+            IdleHostView view,
             @Main Resources resources,
-            @Main Looper looper,
             @Named(IDLE_VIEW) Provider<View> idleViewProvider,
-            Choreographer choreographer,
             KeyguardStateController keyguardStateController,
             StatusBarStateController statusBarStateController,
-            DreamHelper dreamHelper,
             AmbientLightModeMonitor ambientLightModeMonitor) {
         super(view);
-        mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
         mPowerManager = powerManager;
         mIdleViewProvider = idleViewProvider;
         mKeyguardStateController = keyguardStateController;
         mStatusBarStateController = statusBarStateController;
-        mLooper = looper;
-        mChoreographer = choreographer;
-        mDreamHelper = dreamHelper;
         mAmbientLightModeMonitor = ambientLightModeMonitor;
 
         mState = STATE_KEYGUARD_SHOWING;
@@ -236,13 +172,8 @@
 
         setState(mState, true);
 
-        mIdleTimeout = resources.getInteger(R.integer.config_idleModeTimeout);
-        mInputMonitorFactory = factory;
-        mDelayableExecutor = delayableExecutor;
-
         if (DEBUG) {
-            Log.d(TAG, "initial state:" + mState + " enabled:" + enabled
-                    + " timeout:" + mIdleTimeout);
+            Log.d(TAG, "initial state:" + mState + " enabled:" + enabled);
         }
     }
 
@@ -255,20 +186,6 @@
     }
 
     private void setState(@State int state, boolean active) {
-        // If waiting for dozing to stop, ignore any state update until dozing is stopped.
-        if (mDozeToDreamLock) {
-            if (state == STATE_DOZING && !active) {
-                if (DEBUG) {
-                    Log.d(TAG, "dozing stopped, now start dreaming");
-                }
-
-                mDozeToDreamLock = false;
-                enableIdleMode(true);
-            }
-
-            return;
-        }
-
         final int oldState = mState;
 
         if (active) {
@@ -281,53 +198,20 @@
             return;
         }
 
-        // Updates STATE_IDLING.
-        final boolean isIdling = getState(STATE_DOZING) || getState(STATE_DREAMING);
-        if (isIdling) {
-            mState |= STATE_IDLING;
-        } else {
-            mState &= ~STATE_IDLING;
-        }
-
-        // Updates STATE_SHOULD_START_IDLING.
-        final boolean stoppedIdling = stoppedIdling(oldState);
-        if (stoppedIdling) {
-            mState &= ~STATE_SHOULD_START_IDLING;
-        } else if (shouldStartIdling(oldState)) {
-            mState |= STATE_SHOULD_START_IDLING;
-        }
-
         if (DEBUG) {
             Log.d(TAG, "set " + getStateName(state) + " to " + active);
             logCurrentState();
         }
 
-        final boolean wasLowLight = getState(STATE_LOW_LIGHT, oldState);
-        final boolean isLowLight = getState(STATE_LOW_LIGHT);
-        final boolean wasIdling = getState(STATE_IDLING, oldState);
-
-        // When the device is idling and no longer in low light, wake up from dozing, wait till
-        // done, and start dreaming.
-        if (wasLowLight && !isLowLight && wasIdling && isIdling) {
-            if (DEBUG) {
-                Log.d(TAG, "idling and no longer in low light, stop dozing");
-            }
-
-            mDozeToDreamLock = true;
-
-            enableLowLightMode(false);
-            return;
-        }
-
         final boolean inCommunalMode = getState(STATE_IDLE_MODE_ENABLED)
                 && getState(STATE_KEYGUARD_SHOWING);
 
         enableDreamMonitoring(inCommunalMode);
         enableLowLightMonitoring(inCommunalMode);
-        enableIdleMonitoring(inCommunalMode && !getState(STATE_IDLING));
-        enableIdleMode(inCommunalMode && !getState(STATE_LOW_LIGHT)
-                && getState(STATE_SHOULD_START_IDLING));
-        enableLowLightMode(inCommunalMode && !stoppedIdling && getState(STATE_LOW_LIGHT));
+
+        if (state == STATE_LOW_LIGHT) {
+            enableLowLightMode(inCommunalMode && active);
+        }
     }
 
     private void enableDreamMonitoring(boolean enable) {
@@ -354,64 +238,6 @@
         }
     }
 
-    private void enableIdleMonitoring(boolean enable) {
-        if (enable && mInputMonitor == null && mInputEventReceiver == null) {
-            if (DEBUG) {
-                Log.d(TAG, "enable idle monitoring");
-            }
-            // Set initial timeout to idle.
-            mCancelEnableIdling = mDelayableExecutor.executeDelayed(mEnableIdlingCallback,
-                    mIdleTimeout);
-
-            // Monitor - any input should reset timer
-            mInputMonitor = mInputMonitorFactory.getInputMonitor(INPUT_MONITOR_IDENTIFIER);
-            mInputEventReceiver = mInputMonitor.getInputReceiver(mLooper, mChoreographer,
-                    v -> {
-                        if (DEBUG) {
-                            Log.d(TAG, "touch detected, resetting timeout");
-                        }
-                        // When input is received, reset timeout.
-                        if (mCancelEnableIdling != null) {
-                            mCancelEnableIdling.run();
-                            mCancelEnableIdling = null;
-                        }
-                        mCancelEnableIdling = mDelayableExecutor.executeDelayed(
-                                mEnableIdlingCallback, mIdleTimeout);
-                    });
-        } else if (!enable && mInputMonitor != null && mInputEventReceiver != null) {
-            if (DEBUG) {
-                Log.d(TAG, "disable idle monitoring");
-            }
-            // Clean up idle callback and touch monitoring.
-            if (mCancelEnableIdling != null) {
-                mCancelEnableIdling.run();
-                mCancelEnableIdling = null;
-            }
-
-            mInputEventReceiver.dispose();
-            mInputMonitor.dispose();
-            mInputEventReceiver = null;
-            mInputMonitor = null;
-        }
-    }
-
-    private void enableIdleMode(boolean enable) {
-        if (mIdleModeActive == enable) {
-            return;
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, (enable ? "enable" : "disable") + " idle mode");
-        }
-
-        mIdleModeActive = enable;
-
-        if (mIdleModeActive) {
-            // Start dream.
-            mDreamHelper.startDreaming(mContext);
-        }
-    }
-
     private void enableLowLightMonitoring(boolean enable) {
         if (enable == mIsMonitoringLowLight) {
             return;
@@ -429,13 +255,11 @@
     }
 
     private void enableLowLightMode(boolean enable) {
-        if (mLowLightModeActive == enable) {
+        if (enable == getState(STATE_DOZING)) {
             return;
         }
 
-        mLowLightModeActive = enable;
-
-        if (mLowLightModeActive) {
+        if (enable) {
             if (DEBUG) Log.d(TAG, "enter low light, start dozing");
 
             mPowerManager.goToSleep(
@@ -464,19 +288,6 @@
         mStatusBarStateController.removeCallback(mStatusBarCallback);
     }
 
-    // Returns whether the device just stopped idling by comparing the previous state with the
-    // current one.
-    private boolean stoppedIdling(int oldState) {
-        // The device stopped idling if it's no longer dreaming or dozing.
-        return !getState(STATE_DOZING) && !getState(STATE_DREAMING)
-                && (getState(STATE_DOZING, oldState) || getState(STATE_DREAMING, oldState));
-    }
-
-    private boolean shouldStartIdling(int oldState) {
-        // Should start idling immediately if the device went in low light environment.
-        return !getState(STATE_LOW_LIGHT, oldState) && getState(STATE_LOW_LIGHT);
-    }
-
     private String getStateName(@State int state) {
         switch (state) {
             case STATE_IDLE_MODE_ENABLED:
@@ -489,10 +300,6 @@
                 return "STATE_DREAMING";
             case STATE_LOW_LIGHT:
                 return "STATE_LOW_LIGHT";
-            case STATE_IDLING:
-                return "STATE_IDLING";
-            case STATE_SHOULD_START_IDLING:
-                return "STATE_SHOULD_START_IDLING";
             default:
                 return "STATE_UNKNOWN";
         }
@@ -517,8 +324,6 @@
                 + "\t" + getStateLog(STATE_DOZING) + "\n"
                 + "\t" + getStateLog(STATE_DREAMING) + "\n"
                 + "\t" + getStateLog(STATE_LOW_LIGHT) + "\n"
-                + "\t" + getStateLog(STATE_IDLING) + "\n"
-                + "\t" + getStateLog(STATE_SHOULD_START_IDLING) + "\n"
                 + "}");
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 42f455a..1c0b104 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -49,9 +49,9 @@
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -60,7 +60,7 @@
 import java.util.List;
 import java.util.Set;
 
-public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener {
+public class KeyboardUI extends CoreStartable implements InputManager.OnTabletModeChangedListener {
     private static final String TAG = "KeyboardUI";
     private static final boolean DEBUG = false;
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 2cc564b..a6455e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -30,8 +30,8 @@
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
 import javax.inject.Inject
@@ -88,7 +88,8 @@
 class KeyguardUnlockAnimationController @Inject constructor(
     context: Context,
     private val keyguardStateController: KeyguardStateController,
-    private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
+    private val
+    keyguardViewMediator: Lazy<KeyguardViewMediator>,
     private val keyguardViewController: KeyguardViewController,
     private val smartspaceTransitionController: SmartspaceTransitionController,
     private val featureFlags: FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7bb7cc4..e97e762 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -99,8 +99,8 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
@@ -176,7 +176,7 @@
  * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
  * thread of the keyguard.
  */
-public class KeyguardViewMediator extends SystemUI implements Dumpable,
+public class KeyguardViewMediator extends CoreStartable implements Dumpable,
         StatusBarStateController.StateListener {
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index df950d7..02beff9 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -147,7 +147,7 @@
     public static LogcatEchoTracker provideLogcatEchoTracker(
             ContentResolver contentResolver,
             @Main Looper looper) {
-        if (Build.IS_DEBUGGABLE) {
+        if (Build.isDebuggable()) {
             return LogcatEchoTrackerDebug.create(contentResolver, looper);
         } else {
             return new LogcatEchoTrackerProd();
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 553b6d8..ae5f9b6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -37,7 +37,7 @@
 import android.provider.MediaStore;
 import android.util.Log;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -48,7 +48,7 @@
  * Service that offers to play ringtones by {@link Uri}, since our process has
  * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
  */
-public class RingtonePlayer extends SystemUI {
+public class RingtonePlayer extends CoreStartable {
     private static final String TAG = "RingtonePlayer";
     private static final boolean LOGD = false;
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 42dd886..1981269 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -79,6 +79,7 @@
     private final DialogLaunchAnimator mDialogLaunchAnimator;
     private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
     private final boolean mAboveStatusbar;
+    private final boolean mVolumeAdjustmentForRemoteGroupSessions;
     private final NotificationEntryManager mNotificationEntryManager;
     @VisibleForTesting
     final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -111,6 +112,8 @@
         mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
         mUiEventLogger = uiEventLogger;
         mDialogLaunchAnimator = dialogLaunchAnimator;
+        mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
     }
 
     void start(@NonNull Callback cb) {
@@ -477,7 +480,9 @@
     }
 
     boolean isVolumeControlEnabled(@NonNull MediaDevice device) {
-        return !isActiveRemoteDevice(device);
+        // TODO(b/202500642): Also enable volume control for remote non-group sessions.
+        return !isActiveRemoteDevice(device)
+            || mVolumeAdjustmentForRemoteGroupSessions;
     }
 
     private final MediaController.Callback mCb = new MediaController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
index 31e4939..d60172a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
@@ -25,8 +25,8 @@
 import android.media.AudioManager;
 import android.util.Slog;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -40,7 +40,7 @@
  * documented at {@link #handleTaskStackChanged} apply.
  */
 @SysUISingleton
-public class HomeSoundEffectController extends SystemUI {
+public class HomeSoundEffectController extends CoreStartable {
 
     private static final String TAG = "HomeSoundEffectController";
     private final AudioManager mAudioManager;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index c351d13..0e6e8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -24,9 +24,6 @@
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.containsType;
@@ -613,8 +610,6 @@
         mDeviceProvisionedController.addCallback(mUserSetupListener);
         mNotificationShadeDepthController.addListener(mDepthListener);
 
-        updateAccessibilityButtonModeIfNeeded();
-
         return barView;
     }
 
@@ -1405,34 +1400,6 @@
         updateSystemUiStateFlags(a11yFlags);
     }
 
-    private void updateAccessibilityButtonModeIfNeeded() {
-        final int mode = Settings.Secure.getIntForUser(mContentResolver,
-                Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
-                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
-        // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
-        // mode, so we don't need to update it.
-        if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
-            return;
-        }
-
-        // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
-        // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
-        if (QuickStepContract.isGesturalMode(mNavBarMode)
-                && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
-            Settings.Secure.putIntForUser(mContentResolver,
-                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
-                    UserHandle.USER_CURRENT);
-            // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
-            // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
-        } else if (!QuickStepContract.isGesturalMode(mNavBarMode)
-                && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
-            Settings.Secure.putIntForUser(mContentResolver,
-                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
-                    ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-        }
-    }
-
     public void updateSystemUiStateFlags(int a11yFlags) {
         if (a11yFlags < 0) {
             a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
@@ -1550,6 +1517,9 @@
     @Override
     public void onNavigationModeChanged(int mode) {
         mNavBarMode = mode;
+        // update assistant entry points on system navigation radio button click
+        updateAssistantEntrypoints();
+
         if (!QuickStepContract.isGesturalMode(mode)) {
             // Reset the override alpha
             if (getBarTransitions() != null) {
@@ -1557,7 +1527,6 @@
             }
         }
         updateScreenPinningGestures();
-        updateAccessibilityButtonModeIfNeeded();
 
         if (!canShowSecondaryHandle()) {
             resetSecondaryHandle();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 4959c7d..3dc79c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -16,10 +16,14 @@
 
 package com.android.systemui.navigationbar;
 
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -27,6 +31,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
@@ -46,6 +52,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -142,6 +149,8 @@
         }
         final int oldMode = mNavMode;
         mNavMode = mode;
+        updateAccessibilityButtonModeIfNeeded();
+
         mHandler.post(() -> {
             // create/destroy nav bar based on nav mode only in unfolded state
             if (oldMode != mNavMode) {
@@ -157,6 +166,35 @@
         });
     }
 
+    private void updateAccessibilityButtonModeIfNeeded() {
+        ContentResolver contentResolver = mContext.getContentResolver();
+        final int mode = Settings.Secure.getIntForUser(contentResolver,
+                Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+        // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+        // mode, so we don't need to update it.
+        if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+            return;
+        }
+
+        // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+        // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+        if (QuickStepContract.isGesturalMode(mNavMode)
+                && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+            Settings.Secure.putIntForUser(contentResolver,
+                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+                    UserHandle.USER_CURRENT);
+            // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+            // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+        } else if (!QuickStepContract.isGesturalMode(mNavMode)
+                && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+            Settings.Secure.putIntForUser(contentResolver,
+                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+                    ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+        }
+    }
+
     /** @see #initializeTaskbarIfNecessary() */
     private boolean updateNavbarForTaskbar() {
         boolean taskbarShown = initializeTaskbarIfNecessary();
@@ -222,6 +260,8 @@
      */
     public void createNavigationBars(final boolean includeDefaultDisplay,
             RegisterStatusBarResult result) {
+        updateAccessibilityButtonModeIfNeeded();
+
         // Don't need to create nav bar on the default display if we initialize TaskBar.
         final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
                 && !initializeTaskbarIfNecessary();
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 4e1e1cd..b483e59 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -58,9 +58,9 @@
 import com.android.settingslib.Utils;
 import com.android.settingslib.fuelgauge.BatterySaverUtils;
 import com.android.settingslib.utils.PowerUtil;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -247,7 +247,7 @@
                         .setContentText(mContext.getString(R.string.invalid_charger_text))
                         .setColor(mContext.getColor(
                                 com.android.internal.R.color.system_notification_accent_color));
-        SystemUI.overrideNotificationAppName(mContext, nb, false);
+        CoreStartable.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, UserHandle.ALL);
         mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, n, UserHandle.ALL);
@@ -298,7 +298,7 @@
         }
         nb.setOnlyAlertOnce(!mPlaySound);
         mPlaySound = false;
-        SystemUI.overrideNotificationAppName(mContext, nb, false);
+        CoreStartable.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
         mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
@@ -320,7 +320,7 @@
                 mContext.getString(R.string.no_auto_saver_action),
                 pendingBroadcast(ACTION_AUTO_SAVER_NO_THANKS));
 
-        SystemUI.overrideNotificationAppName(mContext, nb, false);
+        CoreStartable.overrideNotificationAppName(mContext, nb, false);
 
         final Notification n = nb.build();
         mNoMan.notifyAsUser(
@@ -397,7 +397,7 @@
                         .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
                         .setColor(Utils.getColorAttrDefaultColor(mContext,
                                 android.R.attr.colorError));
-        SystemUI.overrideNotificationAppName(mContext, nb, false);
+        CoreStartable.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.notifyAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, n, UserHandle.ALL);
     }
@@ -484,7 +484,7 @@
                                 pendingBroadcast(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING))
                         .setColor(Utils.getColorAttrDefaultColor(mContext,
                                 android.R.attr.colorError));
-        SystemUI.overrideNotificationAppName(mContext, nb, false);
+        CoreStartable.overrideNotificationAppName(mContext, nb, false);
         final Notification n = nb.build();
         mNoMan.notifyAsUser(
                 TAG_TEMPERATURE, SystemMessage.NOTE_THERMAL_SHUTDOWN, n, UserHandle.ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 6252654..37a0f59 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -42,9 +42,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.fuelgauge.Estimate;
 import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.CommandQueue;
@@ -62,7 +62,7 @@
 import dagger.Lazy;
 
 @SysUISingleton
-public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
+public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
 
     static final String TAG = "PowerUI";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 8750976..5510eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -45,8 +45,8 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.privacy.PrivacyChipBuilder;
 import com.android.systemui.privacy.PrivacyItem;
@@ -67,7 +67,7 @@
  * recording audio, accessing the camera or accessing the location.
  */
 @SysUISingleton
-public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback,
+public class TvOngoingPrivacyChip extends CoreStartable implements PrivacyItemController.Callback,
         PrivacyChipDrawable.PrivacyChipDrawableListener {
     private static final String TAG = "TvOngoingPrivacyChip";
     private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 6f6dd9c..b7a44a4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -21,7 +21,7 @@
 import android.content.res.Configuration;
 import android.provider.Settings;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.io.FileDescriptor;
@@ -30,7 +30,7 @@
 /**
  * A proxy to a Recents implementation.
  */
-public class Recents extends SystemUI implements CommandQueue.Callbacks {
+public class Recents extends CoreStartable implements CommandQueue.Callbacks {
 
     private final RecentsImplementation mImpl;
     private final CommandQueue mCommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 58a54f6..28bdd53 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -30,8 +30,8 @@
 import android.view.WindowManager;
 
 import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.util.NotificationChannels;
 
 import javax.inject.Inject;
@@ -86,7 +86,7 @@
             b.setContentIntent(pendingIntent);
         }
 
-        SystemUI.overrideNotificationAppName(mContext, b, true);
+        CoreStartable.overrideNotificationAppName(mContext, b, true);
 
         Notification n = new Notification.BigTextStyle(b)
                 .bigText(errorMsg)
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 9933438..10aa12b 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -24,7 +24,7 @@
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.wm.shell.legacysplitscreen.DividerView;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -37,7 +37,7 @@
  * Dispatches shortcut to System UI components
  */
 @SysUISingleton
-public class ShortcutKeyDispatcher extends SystemUI
+public class ShortcutKeyDispatcher extends CoreStartable
         implements ShortcutKeyServiceProxy.Callbacks {
 
     private static final String TAG = "ShortcutKeyDispatcher";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e189b26..75b3592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -596,7 +596,7 @@
 
     @Override
     public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher, boolean isMultiClientImeEnabled) {
+            boolean showImeSwitcher) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
             SomeArgs args = SomeArgs.obtain();
@@ -604,7 +604,6 @@
             args.argi2 = vis;
             args.argi3 = backDisposition;
             args.argi4 = showImeSwitcher ? 1 : 0;
-            args.argi5 = isMultiClientImeEnabled ? 1 : 0;
             args.arg1 = token;
             Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, args);
             m.sendToTarget();
@@ -993,10 +992,10 @@
     }
 
     private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
-            boolean showImeSwitcher, boolean isMultiClientImeEnabled) {
+            boolean showImeSwitcher) {
         if (displayId == INVALID_DISPLAY) return;
 
-        if (!isMultiClientImeEnabled && mLastUpdatedImeDisplayId != displayId
+        if (mLastUpdatedImeDisplayId != displayId
                 && mLastUpdatedImeDisplayId != INVALID_DISPLAY) {
             // Set previous NavBar's IME window status as invisible when IME
             // window switched to another display for single-session IME case.
@@ -1206,8 +1205,7 @@
                     args = (SomeArgs) msg.obj;
                     handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
                             args.argi2 /* vis */, args.argi3 /* backDisposition */,
-                            args.argi4 != 0 /* showImeSwitcher */,
-                            args.argi5 != 0 /* isMultiClientImeEnabled */);
+                            args.argi4 != 0 /* showImeSwitcher */);
                     break;
                 case MSG_SHOW_RECENT_APPS:
                     for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 74ebfe5..1c00887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -818,7 +818,7 @@
     }
 
     private void showTryFingerprintMsg(int msgId, String a11yString) {
-        if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
+        if (mKeyguardUpdateMonitor.isUdfpsSupported()) {
             // if udfps available, there will always be a tappable affordance to unlock
             // For example, the lock icon
             if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 464b2b6..ff3e97a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -35,7 +35,7 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 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.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;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index cbb3aba..da2b85e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -173,7 +173,7 @@
         }
 
         // Record the to-be mState and mLastState
-        recordHistoricalState(state, mState);
+        recordHistoricalState(state /* newState */, mState /* lastState */, false);
 
         // b/139259891
         if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
@@ -206,6 +206,7 @@
     @Override
     public void setUpcomingState(int nextState) {
         mUpcomingState = nextState;
+        recordHistoricalState(mUpcomingState /* newState */, mState /* lastState */, true);
     }
 
     @Override
@@ -505,31 +506,36 @@
         }
     }
 
-    private void recordHistoricalState(int currentState, int lastState) {
+    private void recordHistoricalState(int newState, int lastState, boolean upcoming) {
         mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
         HistoricalState state = mHistoricalRecords[mHistoryIndex];
-        state.mState = currentState;
+        state.mNewState = newState;
         state.mLastState = lastState;
         state.mTimestamp = System.currentTimeMillis();
+        state.mUpcoming = upcoming;
     }
 
     /**
      * For keeping track of our previous state to help with debugging
      */
     private static class HistoricalState {
-        int mState;
+        int mNewState;
         int mLastState;
         long mTimestamp;
+        boolean mUpcoming;
 
         @Override
         public String toString() {
             if (mTimestamp != 0) {
                 StringBuilder sb = new StringBuilder();
-                sb.append("state=").append(mState)
-                        .append(" (").append(describe(mState)).append(")");
-                sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
+                if (mUpcoming) {
+                    sb.append("upcoming-");
+                }
+                sb.append("newState=").append(mNewState)
+                        .append("(").append(describe(mNewState)).append(")");
+                sb.append(" lastState=").append(mLastState).append("(").append(describe(mLastState))
                         .append(")");
-                sb.append("timestamp=")
+                sb.append(" timestamp=")
                         .append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
 
                 return sb.toString();
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 aa86daa..d5cba72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -55,7 +55,7 @@
 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.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.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.collection.notifcollection.CommonNotifCollection;
@@ -73,8 +73,6 @@
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.statusbar.window.StatusBarWindowModule;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
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 e58ea7b..60d1317 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -51,9 +51,9 @@
 
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.statusbar.CommandQueue;
@@ -71,7 +71,7 @@
  * splitted screen.
  */
 @SysUISingleton
-public class InstantAppNotifier extends SystemUI
+public class InstantAppNotifier extends CoreStartable
         implements CommandQueue.Callbacks, KeyguardStateController.Callback {
     private static final String TAG = "InstantAppNotifier";
     public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
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 82f35a8..2437415 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -638,7 +638,7 @@
 
         // Construct the expanded view.
         if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
+            mNotificationRowBinderLazy.get().inflateViews(entry, null, mInflationCallback);
         }
 
         mPendingNotifications.put(key, entry);
@@ -695,7 +695,7 @@
         }
 
         if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
+            mNotificationRowBinderLazy.get().inflateViews(entry, null, mInflationCallback);
         }
 
         updateNotifications("updateNotificationInternal");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 8562a2e..4f3c287 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -16,7 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
-import com.android.internal.statusbar.IStatusBarService;
+import androidx.annotation.NonNull;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
@@ -34,23 +35,13 @@
 @SysUISingleton
 public class NotifInflaterImpl implements NotifInflater {
 
-    private final IStatusBarService mStatusBarService;
-    private final NotifCollection mNotifCollection;
     private final NotifInflationErrorManager mNotifErrorManager;
-    private final NotifPipeline mNotifPipeline;
 
     private NotificationRowBinderImpl mNotificationRowBinder;
 
     @Inject
-    public NotifInflaterImpl(
-            IStatusBarService statusBarService,
-            NotifCollection notifCollection,
-            NotifInflationErrorManager errorManager,
-            NotifPipeline notifPipeline) {
-        mStatusBarService = statusBarService;
-        mNotifCollection = notifCollection;
+    public NotifInflaterImpl(NotifInflationErrorManager errorManager) {
         mNotifErrorManager = errorManager;
-        mNotifPipeline = notifPipeline;
     }
 
     /**
@@ -61,8 +52,9 @@
     }
 
     @Override
-    public void rebindViews(NotificationEntry entry, InflationCallback callback) {
-        inflateViews(entry, callback);
+    public void rebindViews(@NonNull NotificationEntry entry, @NonNull Params params,
+            @NonNull InflationCallback callback) {
+        inflateViews(entry, params, callback);
     }
 
     /**
@@ -70,10 +62,12 @@
      * views are bound.
      */
     @Override
-    public void inflateViews(NotificationEntry entry, InflationCallback callback) {
+    public void inflateViews(@NonNull NotificationEntry entry, @NonNull Params params,
+            @NonNull InflationCallback callback) {
         try {
             requireBinder().inflateViews(
                     entry,
+                    params,
                     wrapInflationCallback(callback));
         } catch (InflationException e) {
             mNotifErrorManager.setInflationError(entry, e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index 50d7324..9ae9fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -58,12 +58,13 @@
  *     appropriately).
  *  3. OnBeforeTransformGroupListeners are fired ([.addOnBeforeTransformGroupsListener])
  *  4. NotifPromoters are called on each notification with a parent ([.addPromoter])
- *  5. Finalize filters are fired on each notification ([.addFinalizeFilter])
- *  6. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
- *  7. Top-level entries are assigned sections by NotifSections ([.setSections])
- *  8. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
- *  9. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
- *  10. The list is handed off to the view layer to be rendered
+ *  5. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
+ *  6. Top-level entries are assigned sections by NotifSections ([.setSections])
+ *  7. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
+ *  8. OnBeforeFinalizeFilterListeners are fired ([.addOnBeforeFinalizeFilterListener])
+ *  9. Finalize filters are fired on each notification ([.addFinalizeFilter])
+ *  10. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
+ *  11. The list is handed off to the view layer to be rendered
  */
 @SysUISingleton
 class NotifPipeline @Inject constructor(
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 15872da..72cd951 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
@@ -28,12 +28,15 @@
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.os.Trace;
 import android.util.ArrayMap;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
@@ -64,6 +67,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -363,22 +367,24 @@
         mPipelineState.incrementTo(STATE_GROUP_STABILIZING);
         stabilizeGroupingNotifs(mNotifList);
 
+        // Step 5: Section & Sort
+        // Assign each top-level entry a section, and copy to all of its children
+        dispatchOnBeforeSort(mReadOnlyNotifList);
+        mPipelineState.incrementTo(STATE_SORTING);
+        assignSections();
+        notifySectionEntriesUpdated();
+        // Sort the list by section and then within section by our list of custom comparators
+        sortListAndGroups();
 
-        // Step 5: Filter out entries after pre-group filtering, grouping and promoting
-        // Now filters can see grouping information to determine whether to filter or not.
+        // Step 6: Filter out entries after pre-group filtering, grouping, promoting, and sorting
+        // Now filters can see grouping, sectioning, and order information to determine whether
+        // to filter or not.
         dispatchOnBeforeFinalizeFilter(mReadOnlyNotifList);
         mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
         filterNotifs(mNotifList, mNewNotifList, mNotifFinalizeFilters);
         applyNewNotifList();
         pruneIncompleteGroups(mNotifList);
 
-        // Step 6: Sort
-        // Assign each top-level entry a section, then sort the list by section and then within
-        // section by our list of custom comparators
-        dispatchOnBeforeSort(mReadOnlyNotifList);
-        mPipelineState.incrementTo(STATE_SORTING);
-        sortListAndNotifySections();
-
         // Step 7: Lock in our group structure and log anything that's changed since the last run
         mPipelineState.incrementTo(STATE_FINALIZING);
         logChanges();
@@ -408,18 +414,15 @@
 
     private void notifySectionEntriesUpdated() {
         Trace.beginSection("ShadeListBuilder.notifySectionEntriesUpdated");
-        NotifSection currentSection = null;
         mTempSectionMembers.clear();
-        for (int i = 0; i < mNotifList.size(); i++) {
-            ListEntry currentEntry = mNotifList.get(i);
-            if (currentSection != currentEntry.getSection()) {
-                if (currentSection != null) {
-                    currentSection.getSectioner().onEntriesUpdated(mTempSectionMembers);
-                    mTempSectionMembers.clear();
+        for (NotifSection section : mNotifSections) {
+            for (ListEntry entry : mNotifList) {
+                if (section == entry.getSection()) {
+                    mTempSectionMembers.add(entry);
                 }
-                currentSection = currentEntry.getSection();
             }
-            mTempSectionMembers.add(currentEntry);
+            section.getSectioner().onEntriesUpdated(mTempSectionMembers);
+            mTempSectionMembers.clear();
         }
         Trace.endSection();
     }
@@ -653,16 +656,15 @@
                 final List<NotificationEntry> children = group.getRawChildren();
 
                 if (group.getSummary() != null && children.size() == 0) {
-                    shadeList.remove(i);
-                    i--;
-
                     NotificationEntry summary = group.getSummary();
                     summary.setParent(ROOT_ENTRY);
-                    shadeList.add(summary);
+                    // The list may be sorted; replace the group with the summary, in its place
+                    shadeList.set(i, summary);
 
                     group.setSummary(null);
                     annulAddition(group, shadeList);
 
+                    i--;  // The node we visited is gone, so be sure to visit this index again.
                 } else if (group.getSummary() == null
                         || children.size() < MIN_CHILDREN_FOR_GROUP) {
 
@@ -680,7 +682,6 @@
                     // its children (if any) directly to top-level.
 
                     shadeList.remove(i);
-                    i--;
 
                     if (group.getSummary() != null) {
                         final NotificationEntry summary = group.getSummary();
@@ -691,11 +692,14 @@
                     for (int j = 0; j < children.size(); j++) {
                         final NotificationEntry child = children.get(j);
                         child.setParent(ROOT_ENTRY);
-                        shadeList.add(child);
+                        // The list may be sorted, so add the children in order where the group was.
+                        shadeList.add(i + j, child);
                     }
                     children.clear();
 
                     annulAddition(group, shadeList);
+
+                    i--;  // The node we visited is gone, so be sure to visit this index again.
                 }
             }
         }
@@ -765,9 +769,9 @@
         }
     }
 
-    private void sortListAndNotifySections() {
-        Trace.beginSection("ShadeListBuilder.sortListAndNotifySections");
-        // Assign sections to top-level elements and sort their children
+    private void assignSections() {
+        Trace.beginSection("ShadeListBuilder.assignSections");
+        // Assign sections to top-level elements and their children
         for (ListEntry entry : mNotifList) {
             NotifSection section = applySections(entry);
             if (entry instanceof GroupEntry) {
@@ -775,27 +779,64 @@
                 for (NotificationEntry child : parent.getChildren()) {
                     setEntrySection(child, section);
                 }
+            }
+        }
+        Trace.endSection();
+    }
+
+    private void sortListAndGroups() {
+        Trace.beginSection("ShadeListBuilder.sortListAndGroups");
+        // Assign sections to top-level elements and sort their children
+        for (ListEntry entry : mNotifList) {
+            if (entry instanceof GroupEntry) {
+                GroupEntry parent = (GroupEntry) entry;
                 parent.sortChildren(mGroupChildrenComparator);
             }
         }
         mNotifList.sort(mTopLevelComparator);
         assignIndexes(mNotifList);
 
-        notifySectionEntriesUpdated();
+        // Check for suppressed order changes
+        if (!mNotifStabilityManager.isEveryChangeAllowed()) {
+            mForceReorderable = true;
+            boolean isSorted = isSorted(mNotifList, mTopLevelComparator);
+            mForceReorderable = false;
+            if (!isSorted) {
+                mNotifStabilityManager.onEntryReorderSuppressed();
+            }
+        }
         Trace.endSection();
     }
 
+    /** Determine whether the items in the list are sorted according to the comparator */
+    @VisibleForTesting
+    public static <T> boolean isSorted(List<T> items, Comparator<T> comparator) {
+        if (items.size() <= 1) {
+            return true;
+        }
+        Iterator<T> iterator = items.iterator();
+        T previous = iterator.next();
+        T current;
+        while (iterator.hasNext()) {
+            current = iterator.next();
+            if (comparator.compare(previous, current) > 0) {
+                return false;
+            }
+            previous = current;
+        }
+        return true;
+    }
+
     /**
      * Assign the index of each notification relative to the total order
-     * @param notifList
      */
-    private void assignIndexes(List<ListEntry> notifList) {
+    private static void assignIndexes(List<ListEntry> notifList) {
         if (notifList.size() == 0) return;
-        NotifSection currentSection = notifList.get(0).getSection();
+        NotifSection currentSection = requireNonNull(notifList.get(0).getSection());
         int sectionMemberIndex = 0;
         for (int i = 0; i < notifList.size(); i++) {
             ListEntry entry = notifList.get(i);
-            NotifSection section = entry.getSection();
+            NotifSection section = requireNonNull(entry.getSection());
             if (section.getIndex() != currentSection.getIndex()) {
                 sectionMemberIndex = 0;
                 currentSection = section;
@@ -960,8 +1001,14 @@
         return cmp;
     };
 
+    /**
+     * A flag that is set to true when we want to run the comparators as if all reordering is
+     * allowed.  This is used to check if the list is "out of order" after the sort is complete.
+     */
+    private boolean mForceReorderable = false;
+
     private boolean canReorder(ListEntry entry) {
-        return mNotifStabilityManager.isEntryReorderingAllowed(entry);
+        return mForceReorderable || mNotifStabilityManager.isEntryReorderingAllowed(entry);
     }
 
     private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
@@ -1031,8 +1078,11 @@
     private void setEntrySection(ListEntry entry, NotifSection finalSection) {
         entry.getAttachState().setSection(finalSection);
         NotificationEntry representativeEntry = entry.getRepresentativeEntry();
-        if (representativeEntry != null && finalSection != null) {
-            representativeEntry.setBucket(finalSection.getBucket());
+        if (representativeEntry != null) {
+            representativeEntry.getAttachState().setSection(finalSection);
+            if (finalSection != null) {
+                representativeEntry.setBucket(finalSection.getBucket());
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index afdfb3b..644f248 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -26,6 +26,9 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.dagger.SysUISingleton;
@@ -35,6 +38,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -46,7 +51,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import javax.inject.Inject;
 
@@ -66,14 +70,24 @@
     private final NotifInflater mNotifInflater;
     private final NotifInflationErrorManager mNotifErrorManager;
     private final NotifViewBarn mViewBarn;
-    private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
+    private final NotifUiAdjustmentProvider mAdjustmentProvider;
+    private final ArrayMap<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
+
+    /**
+     * The map of notifications to the NotifUiAdjustment (i.e. parameters) that were calculated
+     * when the inflation started.  If an update of any kind results in the adjustment changing,
+     * then the row must be reinflated.  If the row is being inflated, then the inflation must be
+     * aborted and restarted.
+     */
+    private final ArrayMap<NotificationEntry, NotifUiAdjustment> mInflationAdjustments =
+            new ArrayMap<>();
 
     /**
      * The set of notifications that are currently inflating something. Note that this is
      * separate from inflation state as a view could either be uninflated or inflated and still be
      * inflating something.
      */
-    private final Set<NotificationEntry> mInflatingNotifs = new ArraySet<>();
+    private final ArraySet<NotificationEntry> mInflatingNotifs = new ArraySet<>();
 
     private final IStatusBarService mStatusBarService;
 
@@ -92,12 +106,14 @@
             NotifInflater notifInflater,
             NotifInflationErrorManager errorManager,
             NotifViewBarn viewBarn,
+            NotifUiAdjustmentProvider adjustmentProvider,
             IStatusBarService service) {
         this(
                 logger,
                 notifInflater,
                 errorManager,
                 viewBarn,
+                adjustmentProvider,
                 service,
                 CHILD_BIND_CUTOFF,
                 MAX_GROUP_INFLATION_DELAY);
@@ -109,6 +125,7 @@
             NotifInflater notifInflater,
             NotifInflationErrorManager errorManager,
             NotifViewBarn viewBarn,
+            NotifUiAdjustmentProvider adjustmentProvider,
             IStatusBarService service,
             int childBindCutoff,
             long maxGroupInflationDelay) {
@@ -116,6 +133,7 @@
         mNotifInflater = notifInflater;
         mNotifErrorManager = errorManager;
         mViewBarn = viewBarn;
+        mAdjustmentProvider = adjustmentProvider;
         mStatusBarService = service;
         mChildBindCutoff = childBindCutoff;
         mMaxGroupInflationDelay = maxGroupInflationDelay;
@@ -160,6 +178,7 @@
         public void onEntryCleanUp(NotificationEntry entry) {
             mInflationStates.remove(entry);
             mViewBarn.removeViewForEntry(entry);
+            mInflationAdjustments.remove(entry);
         }
     };
 
@@ -269,39 +288,78 @@
     }
 
     private void inflateRequiredNotifViews(NotificationEntry entry) {
+        NotifUiAdjustment newAdjustment = mAdjustmentProvider.calculateAdjustment(entry);
         if (mInflatingNotifs.contains(entry)) {
             // Already inflating this entry
+            String errorIfNoOldAdjustment = "Inflating notification has no adjustments";
+            if (needToReinflate(entry, newAdjustment, errorIfNoOldAdjustment)) {
+                inflateEntry(entry, newAdjustment, "adjustment changed while inflating");
+            }
             return;
         }
         @InflationState int state = mInflationStates.get(entry);
         switch (state) {
             case STATE_UNINFLATED:
-                inflateEntry(entry, "entryAdded");
+                inflateEntry(entry, newAdjustment, "entryAdded");
                 break;
             case STATE_INFLATED_INVALID:
-                rebind(entry, "entryUpdated");
+                rebind(entry, newAdjustment, "entryUpdated");
                 break;
             case STATE_INFLATED:
+                String errorIfNoOldAdjustment = "Fully inflated notification has no adjustments";
+                if (needToReinflate(entry, newAdjustment, errorIfNoOldAdjustment)) {
+                    rebind(entry, newAdjustment, "adjustment changed after inflated");
+                }
+                break;
             case STATE_ERROR:
+                if (needToReinflate(entry, newAdjustment, null)) {
+                    inflateEntry(entry, newAdjustment, "adjustment changed after error");
+                }
+                break;
             default:
                 // Nothing to do.
         }
     }
 
-    private void inflateEntry(NotificationEntry entry, String reason) {
-        abortInflation(entry, reason);
-        mInflatingNotifs.add(entry);
-        mNotifInflater.inflateViews(entry, this::onInflationFinished);
+    private boolean needToReinflate(@NonNull NotificationEntry entry,
+            @NonNull NotifUiAdjustment newAdjustment, @Nullable String oldAdjustmentMissingError) {
+        NotifUiAdjustment oldAdjustment = mInflationAdjustments.get(entry);
+        if (oldAdjustment == null) {
+            if (oldAdjustmentMissingError == null) {
+                return true;
+            } else {
+                throw new IllegalStateException(oldAdjustmentMissingError);
+            }
+        }
+        return NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment);
     }
 
-    private void rebind(NotificationEntry entry, String reason) {
+    private void inflateEntry(NotificationEntry entry,
+            NotifUiAdjustment newAdjustment,
+            String reason) {
+        abortInflation(entry, reason);
+        mInflationAdjustments.put(entry, newAdjustment);
         mInflatingNotifs.add(entry);
-        mNotifInflater.rebindViews(entry, this::onInflationFinished);
+        NotifInflater.Params params = getInflaterParams(newAdjustment, reason);
+        mNotifInflater.inflateViews(entry, params, this::onInflationFinished);
+    }
+
+    private void rebind(NotificationEntry entry,
+            NotifUiAdjustment newAdjustment,
+            String reason) {
+        mInflationAdjustments.put(entry, newAdjustment);
+        mInflatingNotifs.add(entry);
+        NotifInflater.Params params = getInflaterParams(newAdjustment, reason);
+        mNotifInflater.rebindViews(entry, params, this::onInflationFinished);
+    }
+
+    NotifInflater.Params getInflaterParams(NotifUiAdjustment adjustment, String reason) {
+        return new NotifInflater.Params(adjustment.isMinimized(), reason);
     }
 
     private void abortInflation(NotificationEntry entry, String reason) {
         mLogger.logInflationAborted(entry.getKey(), reason);
-        entry.abortTask();
+        mNotifInflater.abortInflation(entry);
         mInflatingNotifs.remove(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 79eb089..c60ebcd 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
@@ -19,12 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 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.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
 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;
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.notification.dagger.SilentHeader;
 import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
 
+import java.util.Collections;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -50,6 +51,7 @@
     public static final boolean SHOW_ALL_SECTIONS = false;
     private final StatusBarStateController mStatusBarStateController;
     private final HighPriorityProvider mHighPriorityProvider;
+    private final NotifUiAdjustmentProvider mAdjustmentProvider;
     private final NodeController mSilentNodeController;
     private final SectionHeaderController mSilentHeaderController;
     private final NodeController mAlertingHeaderController;
@@ -60,11 +62,13 @@
     public RankingCoordinator(
             StatusBarStateController statusBarStateController,
             HighPriorityProvider highPriorityProvider,
+            NotifUiAdjustmentProvider adjustmentProvider,
             @AlertingHeader NodeController alertingHeaderController,
             @SilentHeader SectionHeaderController silentHeaderController,
             @SilentHeader NodeController silentNodeController) {
         mStatusBarStateController = statusBarStateController;
         mHighPriorityProvider = highPriorityProvider;
+        mAdjustmentProvider = adjustmentProvider;
         mAlertingHeaderController = alertingHeaderController;
         mSilentNodeController = silentNodeController;
         mSilentHeaderController = silentHeaderController;
@@ -73,10 +77,10 @@
     @Override
     public void attach(NotifPipeline pipeline) {
         mStatusBarStateController.addCallback(mStatusBarStateCallback);
+        mAdjustmentProvider.setLowPrioritySections(Collections.singleton(mMinimizedNotifSectioner));
 
         pipeline.addPreGroupFilter(mSuspendedFilter);
         pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
-        pipeline.addOnBeforeSortListener(entries -> resetClearAllFlags());
     }
 
     public NotifSectioner getAlertingSectioner() {
@@ -126,6 +130,7 @@
         @Nullable
         @Override
         public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+            mHasSilentEntries = false;
             for (int i = 0; i < entries.size(); i++) {
                 if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
                     mHasSilentEntries = true;
@@ -154,6 +159,7 @@
         @Nullable
         @Override
         public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+            mHasMinimizedEntries = false;
             for (int i = 0; i < entries.size(); i++) {
                 if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
                     mHasMinimizedEntries = true;
@@ -189,12 +195,6 @@
         }
     };
 
-    @VisibleForTesting
-    protected void resetClearAllFlags() {
-        mHasSilentEntries = false;
-        mHasMinimizedEntries = false;
-    }
-
     private final StatusBarStateController.StateListener mStatusBarStateCallback =
             new StatusBarStateController.StateListener() {
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 32b1cf6..75489b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -19,11 +19,12 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
 
-import android.annotation.NonNull;
-
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
@@ -34,6 +35,8 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -53,7 +56,7 @@
  */
 // TODO(b/204468557): Move to @CoordinatorScope
 @SysUISingleton
-public class VisualStabilityCoordinator implements Coordinator {
+public class VisualStabilityCoordinator implements Coordinator, Dumpable {
     private final DelayableExecutor mDelayableExecutor;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final StatusBarStateController mStatusBarStateController;
@@ -66,6 +69,7 @@
     private boolean mReorderingAllowed;
     private boolean mIsSuppressingGroupChange = false;
     private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>();
+    private boolean mIsSuppressingEntryReorder = false;
 
     // key: notification key that can temporarily change its section
     // value: runnable that when run removes its associated RemoveOverrideSuppressionRunnable
@@ -77,6 +81,7 @@
 
     @Inject
     public VisualStabilityCoordinator(
+            DumpManager dumpManager,
             HeadsUpManager headsUpManager,
             WakefulnessLifecycle wakefulnessLifecycle,
             StatusBarStateController statusBarStateController,
@@ -86,6 +91,8 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mStatusBarStateController = statusBarStateController;
         mDelayableExecutor = delayableExecutor;
+
+        dumpManager.registerDumpable(this);
     }
 
     @Override
@@ -99,7 +106,6 @@
 
         pipeline.setVisualStabilityManager(mNotifStabilityManager);
     }
-    // TODO(b/203828145): Ensure stability manager handles minimized state changes
     // TODO(b/203826051): Ensure stability manager can allow reordering off-screen
     //  HUNs to the top of the shade
     private final NotifStabilityManager mNotifStabilityManager =
@@ -108,6 +114,7 @@
                 public void onBeginRun() {
                     mIsSuppressingGroupChange = false;
                     mEntriesWithSuppressedSectionChange.clear();
+                    mIsSuppressingEntryReorder = false;
                 }
 
                 @Override
@@ -124,7 +131,7 @@
                             mReorderingAllowed
                                     || mHeadsUpManager.isAlerting(entry.getKey())
                                     || mEntriesThatCanChangeSection.containsKey(entry.getKey());
-                    if (isSectionChangeAllowedForEntry) {
+                    if (!isSectionChangeAllowedForEntry) {
                         mEntriesWithSuppressedSectionChange.add(entry.getKey());
                     }
                     return isSectionChangeAllowedForEntry;
@@ -134,11 +141,22 @@
                 public boolean isEntryReorderingAllowed(ListEntry section) {
                     return mReorderingAllowed;
                 }
+
+                @Override
+                public boolean isEveryChangeAllowed() {
+                    return mReorderingAllowed;
+                }
+
+                @Override
+                public void onEntryReorderSuppressed() {
+                    mIsSuppressingEntryReorder = true;
+                }
             };
 
     private void updateAllowedStates() {
         mReorderingAllowed = isReorderingAllowed();
-        if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange())) {
+        if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange()
+                || mIsSuppressingEntryReorder)) {
             mNotifStabilityManager.invalidateList();
         }
     }
@@ -211,4 +229,23 @@
             updateAllowedStates();
         }
     };
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("reorderingAllowed: " + mReorderingAllowed);
+        pw.println("  screenOn: " + mScreenOn);
+        pw.println("  panelExpanded: " + mPanelExpanded);
+        pw.println("  pulsing: " + mPulsing);
+        pw.println("isSuppressingGroupChange: " + mIsSuppressingGroupChange);
+        pw.println("isSuppressingEntryReorder: " + mIsSuppressingEntryReorder);
+        pw.println("entriesWithSuppressedSectionChange: "
+                + mEntriesWithSuppressedSectionChange.size());
+        for (String key : mEntriesWithSuppressedSectionChange) {
+            pw.println("  " + key);
+        }
+        pw.println("entriesThatCanChangeSection: " + mEntriesThatCanChangeSection.size());
+        for (String key : mEntriesThatCanChangeSection.keySet()) {
+            pw.println("  " + key);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index e3d76113..c59f184 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -14,22 +14,22 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification.collection.inflation;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
 
 /**
- * Used by the {@link PreparationCoordinator}.  When notifications are added or updated, the
+ * Used by the [PreparationCoordinator].  When notifications are added or updated, the
  * NotifInflater is asked to (re)inflated and prepare their views.  This inflation occurs off the
  * main thread. When the inflation is finished, NotifInflater will trigger its InflationCallback.
  */
-public interface NotifInflater {
+interface NotifInflater {
     /**
      * Called to rebind the entry's views.
      *
      * @param callback callback called after inflation finishes
      */
-    void rebindViews(NotificationEntry entry, InflationCallback callback);
+    fun rebindViews(entry: NotificationEntry, params: Params, callback: InflationCallback)
 
     /**
      * Called to inflate the views of an entry.  Views are not considered inflated until all of its
@@ -37,18 +37,23 @@
      *
      * @param callback callback called after inflation finishes
      */
-    void inflateViews(NotificationEntry entry, InflationCallback callback);
+    fun inflateViews(entry: NotificationEntry, params: Params, callback: InflationCallback)
 
     /**
      * Request to stop the inflation of an entry.  For example, called when a notification is
      * removed and no longer needs to be inflated.
      */
-    void abortInflation(NotificationEntry entry);
+    fun abortInflation(entry: NotificationEntry)
 
     /**
      * Callback once all the views are inflated and bound for a given NotificationEntry.
      */
     interface InflationCallback {
-        void onInflationFinished(NotificationEntry entry);
+        fun onInflationFinished(entry: NotificationEntry)
     }
-}
+
+    /**
+     * A class holding parameters used when inflating the notification row
+     */
+    class Params(val isLowPriority: Boolean, val reason: String)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
new file mode 100644
index 0000000..9d86b78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021 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.inflation
+
+import android.app.Notification
+import android.app.RemoteInput
+import android.graphics.drawable.Icon
+import android.text.TextUtils
+
+/**
+ * An immutable object which contains minimal state extracted from an entry that represents state
+ * which can change without a direct app update (e.g. with a ranking update).
+ * Diffing two entries determines if view re-inflation is needed.
+ */
+class NotifUiAdjustment internal constructor(
+    val key: String,
+    val smartActions: List<Notification.Action>,
+    val smartReplies: List<CharSequence>,
+    val isConversation: Boolean,
+    val isMinimized: Boolean
+) {
+    companion object {
+        @JvmStatic
+        fun needReinflate(
+            oldAdjustment: NotifUiAdjustment,
+            newAdjustment: NotifUiAdjustment
+        ): Boolean = when {
+            oldAdjustment === newAdjustment -> false
+            oldAdjustment.isConversation != newAdjustment.isConversation -> true
+            oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
+            areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
+            newAdjustment.smartReplies != oldAdjustment.smartReplies -> true
+            else -> false
+        }
+
+        private fun areDifferent(
+            first: List<Notification.Action>,
+            second: List<Notification.Action>
+        ): Boolean = when {
+            first === second -> false
+            first.size != second.size -> true
+            else -> first.asSequence().zip(second.asSequence()).any {
+                (!TextUtils.equals(it.first.title, it.second.title)) ||
+                        (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
+                        (it.first.actionIntent != it.second.actionIntent) ||
+                        (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+            }
+        }
+
+        private fun areDifferent(first: Icon?, second: Icon?): Boolean = when {
+            first === second -> false
+            first == null || second == null -> true
+            else -> !first.sameAs(second)
+        }
+
+        private fun areDifferent(
+            first: Array<RemoteInput>?,
+            second: Array<RemoteInput>?
+        ): Boolean = when {
+            first === second -> false
+            first == null || second == null -> true
+            first.size != second.size -> true
+            else -> first.asSequence().zip(second.asSequence()).any {
+                (!TextUtils.equals(it.first.label, it.second.label)) ||
+                        (areDifferent(it.first.choices, it.second.choices))
+            }
+        }
+
+        private fun areDifferent(
+            first: Array<CharSequence>?,
+            second: Array<CharSequence>?
+        ): Boolean = when {
+            first === second -> false
+            first == null || second == null -> true
+            first.size != second.size -> true
+            else -> first.asSequence().zip(second.asSequence()).any {
+                !TextUtils.equals(it.first, it.second)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
new file mode 100644
index 0000000..3290cdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 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.inflation
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import javax.inject.Inject
+
+/**
+ * A class which provides an adjustment object to the preparation coordinator which is uses
+ * to ensure that notifications are reinflated when ranking-derived information changes.
+ */
+@SysUISingleton
+open class NotifUiAdjustmentProvider @Inject constructor() {
+
+    private lateinit var lowPrioritySections: Set<NotifSectioner>
+
+    /**
+     * Feed the provider the information it needs about which sections should have minimized top
+     * level views, so that it can calculate the correct minimized value in the adjustment.
+     */
+    fun setLowPrioritySections(sections: Collection<NotifSectioner>) {
+        lowPrioritySections = sections.toSet()
+    }
+
+    private fun isEntryMinimized(entry: NotificationEntry): Boolean {
+        val section = entry.section ?: error("Entry must have a section to determine if minimized")
+        val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
+        val isLowPrioritySection = lowPrioritySections.contains(section.sectioner)
+        val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
+        val isGroupSummary = parent.summary == entry
+        return isLowPrioritySection && (isTopLevelEntry || isGroupSummary)
+    }
+
+    /**
+     * Returns a adjustment object for the given entry.  This can be compared to a previous instance
+     * from the same notification using [NotifUiAdjustment.needReinflate] to determine if it
+     * should be reinflated.
+     */
+    fun calculateAdjustment(entry: NotificationEntry) = NotifUiAdjustment(
+        key = entry.key,
+        smartActions = entry.ranking.smartActions,
+        smartReplies = entry.ranking.smartReplies,
+        isConversation = entry.ranking.isConversation,
+        isMinimized = isEntryMinimized(entry)
+    )
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index 1215ade..3a4701c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -32,12 +32,10 @@
     /**
      * Called when a notification has been added or updated. The binder must asynchronously inflate
      * and bind the views associated with the notification.
-     *
-     * TODO: The caller is notified when the inflation completes, but this is currently a very
-     * roundabout business. Add an explicit completion/failure callback to this method.
      */
     void inflateViews(
             NotificationEntry entry,
+            NotifInflater.Params params,
             NotificationRowContentBinder.InflationCallback callback)
             throws InflationException;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e5425cf..5c8e8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -25,6 +25,7 @@
 
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -32,6 +33,7 @@
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
 import com.android.systemui.statusbar.notification.icon.IconManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
@@ -53,6 +55,7 @@
     private static final String TAG = "NotificationViewManager";
 
     private final Context mContext;
+    private final FeatureFlags mFeatureFlags;
     private final NotificationMessagingUtil mMessagingUtil;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
     private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -72,6 +75,7 @@
     @Inject
     public NotificationRowBinderImpl(
             Context context,
+            FeatureFlags featureFlags,
             NotificationMessagingUtil notificationMessagingUtil,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
@@ -82,6 +86,7 @@
             IconManager iconManager,
             LowPriorityInflationHelper lowPriorityInflationHelper) {
         mContext = context;
+        mFeatureFlags = featureFlags;
         mNotifBindPipeline = notifBindPipeline;
         mRowContentBindStage = rowContentBindStage;
         mMessagingUtil = notificationMessagingUtil;
@@ -116,8 +121,13 @@
     @Override
     public void inflateViews(
             NotificationEntry entry,
+            NotifInflater.Params params,
             NotificationRowContentBinder.InflationCallback callback)
             throws InflationException {
+        if (params == null) {
+            // weak assert that the params should always be passed in the new pipeline
+            mFeatureFlags.checkLegacyPipelineEnabled();
+        }
         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
 
         if (entry.rowExists()) {
@@ -125,7 +135,7 @@
             ExpandableNotificationRow row = entry.getRow();
             row.reset();
             updateRow(entry, row);
-            inflateContentViews(entry, row, callback);
+            inflateContentViews(entry, params, row, callback);
         } else {
             mIconManager.createIcons(entry);
             mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
@@ -144,7 +154,7 @@
                         entry.setRowController(rowController);
                         bindRow(entry, row);
                         updateRow(entry, row);
-                        inflateContentViews(entry, row, callback);
+                        inflateContentViews(entry, params, row, callback);
                     });
         }
     }
@@ -177,12 +187,13 @@
             NotificationUiAdjustment oldAdjustment,
             NotificationUiAdjustment newAdjustment,
             NotificationRowContentBinder.InflationCallback callback) {
+        mFeatureFlags.checkLegacyPipelineEnabled();
         if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
             if (entry.rowExists()) {
                 ExpandableNotificationRow row = entry.getRow();
                 row.reset();
                 updateRow(entry, row);
-                inflateContentViews(entry, row, callback);
+                inflateContentViews(entry, null, row, callback);
             } else {
                 // Once the RowInflaterTask is done, it will pick up the updated entry, so
                 // no-op here.
@@ -216,15 +227,24 @@
      */
     private void inflateContentViews(
             NotificationEntry entry,
+            NotifInflater.Params inflaterParams,
             ExpandableNotificationRow row,
             @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
         final boolean useIncreasedCollapsedHeight =
                 mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance());
-        // If this is our first time inflating, we don't actually know the groupings for real
-        // yet, so we might actually inflate a low priority content view incorrectly here and have
-        // to correct it later in the pipeline. On subsequent inflations (i.e. updates), this
-        // should inflate the correct view.
-        final boolean isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
+        final boolean isLowPriority;
+        if (inflaterParams != null) {
+            // NEW pipeline
+            isLowPriority = inflaterParams.isLowPriority();
+        } else {
+            // LEGACY pipeline
+            mFeatureFlags.checkLegacyPipelineEnabled();
+            // If this is our first time inflating, we don't actually know the groupings for real
+            // yet, so we might actually inflate a low priority content view incorrectly here and
+            // have to correct it later in the pipeline. On subsequent inflations (i.e. updates),
+            // this should inflate the correct view.
+            isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
+        }
 
         RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
         params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
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/legacy/LowPriorityInflationHelper.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
index 518c3f1..dd1f948 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification.collection.inflation;
+package com.android.systemui.statusbar.notification.collection.legacy;
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.flags.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;
@@ -61,6 +59,7 @@
     public void recheckLowPriorityViewAndInflate(
             NotificationEntry entry,
             ExpandableNotificationRow row) {
+        mFeatureFlags.checkLegacyPipelineEnabled();
         RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
         final boolean shouldBeLowPriority = shouldUseLowPriorityView(entry);
         if (!row.isRemoved() && row.isLowPriority() != shouldBeLowPriority) {
@@ -74,12 +73,7 @@
      * Whether the notification should inflate a low priority version of its content views.
      */
     public boolean shouldUseLowPriorityView(NotificationEntry entry) {
-        boolean isGroupChild;
-        if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            isGroupChild = (entry.getParent() != GroupEntry.ROOT_ENTRY);
-        } else {
-            isGroupChild = mGroupManager.isChildInGroup(entry);
-        }
-        return entry.isAmbient() && !isGroupChild;
+        mFeatureFlags.checkLegacyPipelineEnabled();
+        return entry.isAmbient() && !mGroupManager.isChildInGroup(entry);
     }
 }
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 6424e37..8444287 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
@@ -27,8 +27,7 @@
     val label: String
         get() = "Section($index, $bucket, \"${sectioner.name}\")"
 
-    val headerController: NodeController?
-        get() = sectioner.headerNodeController
+    val headerController: NodeController? = sectioner.headerNodeController
 
     @PriorityBucket val bucket: Int = sectioner.bucket
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 027ac0f..798bfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -82,8 +82,8 @@
     public static final int STATE_GROUPING = 4;
     public static final int STATE_TRANSFORMING = 5;
     public static final int STATE_GROUP_STABILIZING = 6;
-    public static final int STATE_FINALIZE_FILTERING = 7;
-    public static final int STATE_SORTING = 8;
+    public static final int STATE_SORTING = 7;
+    public static final int STATE_FINALIZE_FILTERING = 8;
     public static final int STATE_FINALIZING = 9;
 
     @IntDef(prefix = { "STATE_" }, value = {
@@ -94,8 +94,8 @@
             STATE_GROUPING,
             STATE_TRANSFORMING,
             STATE_GROUP_STABILIZING,
-            STATE_FINALIZE_FILTERING,
             STATE_SORTING,
+            STATE_FINALIZE_FILTERING,
             STATE_FINALIZING,
     })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
index 520791c..cb2d3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
@@ -60,4 +60,19 @@
      * @return if can re-order
      */
     public abstract boolean isEntryReorderingAllowed(ListEntry entry);
+
+    /**
+     * Called by the pipeline to determine if every call to the other stability methods would
+     * return true, regardless of parameters.  This allows the pipeline to skip any pieces of
+     * work related to stability.
+     *
+     * @return true if all other methods will return true for any parameters.
+     */
+    public abstract boolean isEveryChangeAllowed();
+
+    /**
+     * Called by the pipeline to inform the stability manager that an entry reordering was indeed
+     * suppressed as the result of a previous call to {@link #isEntryReorderingAllowed(ListEntry)}.
+     */
+    public abstract void onEntryReorderSuppressed();
 }
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 11b0429..757f68a 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
@@ -108,7 +108,7 @@
         notifBindPipelineInitializer.initialize()
         animatedImageNotificationManager.bind()
 
-        if (featureFlags.isNewNotifPipelineEnabled) {
+        if (INITIALIZE_NEW_PIPELINE) {
             newNotifPipeline.get().initialize(
                     notificationListener,
                     notificationRowBinder,
@@ -170,4 +170,9 @@
     override fun getActiveNotificationsCount(): Int {
         return entryManager.activeNotificationsCount
     }
+
+    companion object {
+        // NOTE: The new pipeline is always active, even if the old pipeline is *rendering*.
+        private const val INITIALIZE_NEW_PIPELINE = true
+    }
 }
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 5f157a7..408c457 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
@@ -22,6 +22,7 @@
 import android.view.View
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.media.KeyguardMediaController
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
@@ -53,6 +54,7 @@
  * TODO: Move remaining sections logic from NSSL into this class.
  */
 class NotificationSectionsManager @Inject internal constructor(
+    private val featureFlags: FeatureFlags,
     private val statusBarStateController: StatusBarStateController,
     private val configurationController: ConfigurationController,
     private val keyguardMediaController: KeyguardMediaController,
@@ -199,6 +201,7 @@
                 override var targetPosition: Int? = null
 
                 override fun adjustViewPosition() {
+                    featureFlags.checkLegacyPipelineEnabled()
                     val target = targetPosition
                     val current = currentPosition
                     if (target == null) {
@@ -225,6 +228,7 @@
     private fun <T : StackScrollerDecorView> decorViewHeaderState(
         header: T
     ): SectionUpdateState<T> {
+        featureFlags.checkLegacyPipelineEnabled()
         val inner = expandableViewHeaderState(header)
         return object : SectionUpdateState<T> by inner {
             override fun adjustViewPosition() {
@@ -241,6 +245,7 @@
      * bookkeeping and adds/moves/removes section headers if appropriate.
      */
     fun updateSectionBoundaries(reason: String) {
+        featureFlags.checkLegacyPipelineEnabled()
         if (!isUsingMultipleSections) {
             return
         }
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 bc79db3..a97a54a 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
@@ -4631,6 +4631,7 @@
 
     public void addContainerViewAt(View v, int index) {
         Assert.isMainThread();
+        ensureRemovedFromTransientContainer(v);
         addView(v, index);
         if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
             mController.updateShowEmptyShadeView();
@@ -4640,6 +4641,22 @@
         updateSpeedBumpIndex();
     }
 
+    private void ensureRemovedFromTransientContainer(View v) {
+        if (v.getParent() == this && v instanceof SectionHeaderView) {
+            ExpandableView expandableView = (ExpandableView) v;
+            ViewGroup transientContainer = expandableView.getTransientContainer();
+            // If the child is animating away, it will still have a parent, so
+            // detach it first
+            // TODO: We should really cancel the active animations here. This will
+            //  happen automatically when the view's intro animation starts, but
+            //  it's a fragile link.
+            if (transientContainer != null) {
+                transientContainer.removeTransientView(v);
+                expandableView.setTransientContainer(null);
+            }
+        }
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void runAfterAnimationFinished(Runnable runnable) {
         mAnimationFinishedRunnables.add(runnable);
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 6647769..eb7410c 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
@@ -17,6 +17,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.content.res.Resources;
@@ -28,6 +30,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.FalsingManager;
@@ -264,6 +267,22 @@
     }
 
     @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        final boolean previousIsSwiping = isSwiping();
+        boolean ret = super.onInterceptTouchEvent(ev);
+        final View swipedView = getSwipedView();
+        if (!previousIsSwiping && swipedView != null) {
+            InteractionJankMonitor.getInstance().begin(swipedView,
+                    CUJ_NOTIFICATION_SHADE_ROW_SWIPE);
+        }
+        return ret;
+    }
+
+    protected void onDismissChildWithAnimationFinished() {
+        InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE);
+    }
+
+    @Override
     public void dismissChild(final View view, float velocity,
             boolean useAccelerateInterpolator) {
         superDismissChild(view, velocity, useAccelerateInterpolator);
@@ -281,6 +300,11 @@
         super.dismissChild(view, velocity, useAccelerateInterpolator);
     }
 
+    @Override
+    protected void onSnapChildWithAnimationFinished() {
+        InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE);
+    }
+
     @VisibleForTesting
     protected void superSnapChild(final View animView, final float targetLeft, float velocity) {
         super.snapChild(animView, targetLeft, velocity);
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 e26e69f..3953698 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2342,7 +2342,7 @@
         mQs.setExpanded(mQsExpanded);
     }
 
-    private void setQsExpansion(float height) {
+    void setQsExpansion(float height) {
         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
         mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
         if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
@@ -2386,7 +2386,13 @@
         int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
         mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
         setQSClippingBounds();
-        mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+
+        // Only need to notify the notification stack when we're not in split screen mode. If we
+        // do, then the notification panel starts scrolling along with the QS.
+        if (!mShouldUseSplitNotificationShade) {
+            mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+        }
+
         mDepthController.setQsPanelExpansion(qsExpansionFraction);
 
         if (mCommunalViewController != null) {
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 afa333a..b47e71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -128,12 +128,12 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.ActivityIntentHelper;
 import com.android.systemui.AutoReinflateContainer;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.InitController;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DelegateLaunchAnimatorController;
 import com.android.systemui.assist.AssistManager;
@@ -255,7 +255,7 @@
 import dagger.Lazy;
 
 /** */
-public class StatusBar extends SystemUI implements
+public class StatusBar extends CoreStartable implements
         ActivityStarter,
         LifecycleOwner {
     public static final boolean MULTIUSER_DEBUG = false;
@@ -2385,6 +2385,8 @@
 
         if (mLightRevealScrim != null) {
             pw.println(
+                    "mLightRevealScrim.getRevealEffect(): " + mLightRevealScrim.getRevealEffect());
+            pw.println(
                     "mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount());
         }
 
@@ -3370,17 +3372,24 @@
             return;
         }
 
-        if (wakingUp && mWakefulnessLifecycle.getLastWakeReason()
-                == PowerManager.WAKE_REASON_POWER_BUTTON
-                || !wakingUp && mWakefulnessLifecycle.getLastSleepReason()
-                == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
+        final boolean wakingUpFromPowerButton = wakingUp
+                && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
+                && mWakefulnessLifecycle.getLastWakeReason()
+                == PowerManager.WAKE_REASON_POWER_BUTTON;
+        final boolean sleepingFromPowerButton = !wakingUp
+                && mWakefulnessLifecycle.getLastSleepReason()
+                == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON;
+
+        if (wakingUpFromPowerButton || sleepingFromPowerButton) {
             mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
+            mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
         } else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
             // If we're going to sleep, but it's not from the power button, use the default reveal.
             // If we're waking up, only use the default reveal if the biometric controller didn't
             // already set it to the circular reveal because we're waking up from a fingerprint/face
             // auth.
             mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+            mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
         }
     }
 
@@ -4278,7 +4287,9 @@
                 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
             }
 
-            mViewHierarchyManager.updateRowStates();
+            if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+                mViewHierarchyManager.updateRowStates();
+            }
             mScreenPinningRequest.onConfigurationChanged();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index ecd5c98..7c0f923 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -305,6 +305,9 @@
 
     @Override
     public void updateNotificationViews(final String reason) {
+        if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+            return;
+        }
         // The function updateRowStates depends on both of these being non-null, so check them here.
         // We may be called before they are set from DeviceProvisionedController's callback.
         if (mScrimController == null) return;
@@ -325,15 +328,17 @@
         // End old BaseStatusBar.userSwitched
         if (MULTIUSER_DEBUG) mNotificationPanel.setHeaderDebugInfo("USER " + newUserId);
         mCommandQueue.animateCollapsePanels();
-        if (mReinflateNotificationsOnUserSwitched) {
-            updateNotificationsOnDensityOrFontScaleChanged();
-            mReinflateNotificationsOnUserSwitched = false;
+        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+            if (mReinflateNotificationsOnUserSwitched) {
+                updateNotificationsOnDensityOrFontScaleChanged();
+                mReinflateNotificationsOnUserSwitched = false;
+            }
+            if (mDispatchUiModeChangeOnUserSwitched) {
+                updateNotificationsOnUiModeChanged();
+                mDispatchUiModeChangeOnUserSwitched = false;
+            }
+            updateNotificationViews("user switched");
         }
-        if (mDispatchUiModeChangeOnUserSwitched) {
-            updateNotificationsOnUiModeChanged();
-            mDispatchUiModeChangeOnUserSwitched = false;
-        }
-        updateNotificationViews("user switched");
         mMediaManager.clearCurrentMediaNotification();
         mStatusBar.setLockscreenUser(newUserId);
         updateMediaMetaData(true, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 37a763b..8ea7d62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -22,7 +22,7 @@
 import android.os.ServiceManager;
 
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.CommandQueue;
@@ -36,7 +36,7 @@
  * Serves as a collection of UI components, rather than showing its own UI.
  */
 @SysUISingleton
-public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
+public class TvStatusBar extends CoreStartable implements CommandQueue.Callbacks {
 
     private final CommandQueue mCommandQueue;
     private final Lazy<AssistManager> mAssistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
index 7c6f86a..e25a105 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
@@ -24,9 +24,9 @@
 import android.content.Context
 import com.android.internal.messages.nano.SystemMessageProto
 import com.android.internal.net.VpnConfig
+import com.android.systemui.CoreStartable
 import com.android.systemui.Dependency
 import com.android.systemui.R
-import com.android.systemui.SystemUI
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.policy.SecurityController
 import javax.inject.Inject
@@ -35,7 +35,7 @@
  * Observes if a vpn connection is active and displays a notification to the user
  */
 @SysUISingleton
-class VpnStatusObserver @Inject constructor(context: Context) : SystemUI(context),
+class VpnStatusObserver @Inject constructor(context: Context) : CoreStartable(context),
         SecurityController.SecurityControllerCallback {
 
     private var vpnConnected = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
index d985803..8026ba5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
@@ -24,7 +24,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.statusbar.NotificationListener;
 
 import javax.inject.Inject;
@@ -32,7 +32,7 @@
 /**
  * Keeps track of the notifications on TV.
  */
-public class TvNotificationHandler extends SystemUI implements
+public class TvNotificationHandler extends CoreStartable implements
         NotificationListener.NotificationHandler {
     private static final String TAG = "TvNotificationHandler";
     private final NotificationListener mNotificationListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
index c9bc7e6..892fedc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
@@ -25,7 +25,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.CommandQueue;
 
@@ -35,7 +35,7 @@
  * Offers control methods for the notification panel handler on TV devices.
  */
 @SysUISingleton
-public class TvNotificationPanel extends SystemUI implements CommandQueue.Callbacks {
+public class TvNotificationPanel extends CoreStartable implements CommandQueue.Callbacks {
     private static final String TAG = "TvNotificationPanel";
     private final CommandQueue mCommandQueue;
     private final String mNotificationHandlerPackage;
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c5f463f..6d176a7 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -53,8 +53,8 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -93,7 +93,7 @@
  * associated work profiles
  */
 @SysUISingleton
-public class ThemeOverlayController extends SystemUI implements Dumpable {
+public class ThemeOverlayController extends CoreStartable implements Dumpable {
     protected static final String TAG = "ThemeOverlayController";
     private static final boolean DEBUG = true;
 
@@ -259,8 +259,13 @@
                 if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
                 reevaluateSystemTheme(true /* forceReload */);
             } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
-                mAcceptColorEvents = true;
-                Log.i(TAG, "Allowing color events again");
+                if (intent.getBooleanExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, false)) {
+                    mAcceptColorEvents = true;
+                    Log.i(TAG, "Wallpaper changed, allowing color events again");
+                } else {
+                    Log.i(TAG, "Wallpaper changed from background app, "
+                            + "keep deferring color events. Accepting: " + mAcceptColorEvents);
+                }
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 42f6687..0758f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -37,7 +37,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.CommandQueue;
 
@@ -49,7 +49,7 @@
  * Controls display of text toasts.
  */
 @SysUISingleton
-public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
+public class ToastUI extends CoreStartable implements CommandQueue.Callbacks {
     // values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
     private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
     private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 353333f..e59d2f23 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.tv;
 
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.GlobalRootComponent;
 import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
 
@@ -33,5 +33,5 @@
     @Binds
     @IntoMap
     @ClassKey(TvNotificationHandler.class)
-    SystemUI bindTvNotificationHandler(TvNotificationHandler systemui);
+    CoreStartable bindTvNotificationHandler(TvNotificationHandler systemui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 755372b..881ac08 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -44,12 +44,12 @@
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.util.NotificationChannels;
 
 import java.util.List;
 
-public class StorageNotification extends SystemUI {
+public class StorageNotification extends CoreStartable {
     private static final String TAG = "StorageNotification";
 
     private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
@@ -224,7 +224,7 @@
                                 .setCategory(Notification.CATEGORY_SYSTEM)
                                 .setDeleteIntent(buildSnoozeIntent(fsUuid))
                                 .extend(new Notification.TvExtender());
-                SystemUI.overrideNotificationAppName(mContext, builder, false);
+                CoreStartable.overrideNotificationAppName(mContext, builder, false);
 
                 mNotificationManager.notifyAsUser(fsUuid, SystemMessage.NOTE_STORAGE_PRIVATE,
                         builder.build(), UserHandle.ALL);
@@ -252,7 +252,7 @@
                             .setLocalOnly(true)
                             .setCategory(Notification.CATEGORY_ERROR)
                             .extend(new Notification.TvExtender());
-            SystemUI.overrideNotificationAppName(mContext, builder, false);
+            CoreStartable.overrideNotificationAppName(mContext, builder, false);
 
             mNotificationManager.notifyAsUser(disk.getId(), SystemMessage.NOTE_STORAGE_DISK,
                     builder.build(), UserHandle.ALL);
@@ -527,7 +527,7 @@
                         .setCategory(Notification.CATEGORY_PROGRESS)
                         .setProgress(100, status, false)
                         .setOngoing(true);
-        SystemUI.overrideNotificationAppName(mContext, builder, false);
+        CoreStartable.overrideNotificationAppName(mContext, builder, false);
 
         mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
                 builder.build(), UserHandle.ALL);
@@ -577,7 +577,7 @@
                         .setLocalOnly(true)
                         .setCategory(Notification.CATEGORY_SYSTEM)
                         .setAutoCancel(true);
-        SystemUI.overrideNotificationAppName(mContext, builder, false);
+        CoreStartable.overrideNotificationAppName(mContext, builder, false);
 
         mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
                 builder.build(), UserHandle.ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index f97f4d0..ce7e4cf 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -23,13 +23,13 @@
 import android.provider.Settings;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.wm.shell.pip.tv.TvPipNotificationController;
 
 import java.util.Arrays;
 
-public class NotificationChannels extends SystemUI {
+public class NotificationChannels extends CoreStartable {
     public static String ALERTS      = "ALR";
     public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP";
     public static String GENERAL     = "GEN";
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 4318994..612b7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -45,9 +45,9 @@
 import android.view.View;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -565,7 +565,7 @@
 
     /** */
     @SysUISingleton
-    public static class Service extends SystemUI implements Dumpable {
+    public static class Service extends CoreStartable implements Dumpable {
         private final GarbageMonitor mGarbageMonitor;
 
         @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index c378e3b..8007ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -21,8 +21,8 @@
 import android.os.Handler;
 import android.util.Log;
 
+import com.android.systemui.CoreStartable;
 import com.android.systemui.R;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.qs.tiles.DndTile;
 
@@ -32,7 +32,7 @@
 import javax.inject.Inject;
 
 @SysUISingleton
-public class VolumeUI extends SystemUI {
+public class VolumeUI extends CoreStartable {
     private static final String TAG = "VolumeUI";
     private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 74611cc..66c70ed 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -40,8 +40,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.Dependency;
-import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.WMComponent;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -92,7 +92,7 @@
  *       -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
  */
 @SysUISingleton
-public final class WMShell extends SystemUI
+public final class WMShell extends CoreStartable
         implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
     private static final String TAG = WMShell.class.getName();
     private static final int INVALID_SYSUI_STATE_MASK =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1ee6f70..ff5960b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -472,7 +472,8 @@
         mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
         mTestableLooper.processAllMessages();
 
-        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt());
+        verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+                        anyInt());
         verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
     }
 
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 045fb57f..148c6ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -48,6 +48,7 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -61,11 +62,11 @@
 @RunWith(AndroidTestingRunner.class)
 public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
 
-    private static final float DEFAULT_SCALE = 3.0f;
+    private static final float DEFAULT_SCALE = 4.0f;
     private static final float DEFAULT_CENTER_X = 400.0f;
     private static final float DEFAULT_CENTER_Y = 500.0f;
     // The duration couldn't too short, otherwise the ValueAnimator won't work in expectation.
-    private static final long ANIMATION_DURATION_MS = 200;
+    private static final long ANIMATION_DURATION_MS = 300;
 
     private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0);
     private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0);
@@ -84,7 +85,7 @@
     IRemoteMagnificationAnimationCallback mAnimationCallback;
     @Mock
     IRemoteMagnificationAnimationCallback mAnimationCallback2;
-    @Mock
+    @Mock(answer = Answers.RETURNS_SELF)
     SysUiState mSysUiState;
     private SpyWindowMagnificationController mController;
     private WindowMagnificationController mSpyController;
@@ -127,6 +128,28 @@
         verify(mAnimationCallback).onResult(true);
     }
 
+    @Test
+    public void enableWindowMagnificationWithoutCallback_disabled_expectedValues() {
+        enableWindowMagnificationWithoutAnimation();
+
+        verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+    }
+
+    @Test
+    public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
+        final float targetScale = DEFAULT_SCALE + 1.0f;
+        final float targetCenterX = DEFAULT_CENTER_X + 100;
+        final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                            targetCenterX, targetCenterY, null);
+                });
+
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+    }
 
     @Test
     public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
@@ -174,6 +197,52 @@
     }
 
     @Test
+    public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback()
+            throws RemoteException {
+        enableWindowMagnificationWithoutAnimation();
+
+        mInstrumentation.runOnMainSync(() -> {
+            Mockito.reset(mSpyController);
+            mWindowMagnificationAnimationController.enableWindowMagnification(1.0f,
+                    DEFAULT_CENTER_X + 100, DEFAULT_CENTER_Y + 100, mAnimationCallback);
+            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());
+        // It presents the window magnification is disabled.
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+
+        verify(mAnimationCallback).onResult(true);
+    }
+
+    @Test
+    public void
+            enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback()
+            throws RemoteException {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationCallback);
+        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, null);
+        });
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+        verify(mAnimationCallback).onResult(false);
+    }
+
+    @Test
     public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()
             throws RemoteException {
         enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
@@ -231,6 +300,28 @@
     }
 
     @Test
+    public void
+            enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback()
+            throws RemoteException {
+        enableWindowMagnificationWithoutAnimation();
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationCallback);
+        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, null);
+                });
+
+        verify(mAnimationCallback).onResult(false);
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+    }
+
+    @Test
     public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()
             throws RemoteException {
         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
@@ -254,8 +345,7 @@
     @Test
     public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()
             throws RemoteException {
-        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
-                mAnimationCallback);
+        enableWindowMagnificationWithoutAnimation();
         final float targetScale = DEFAULT_SCALE + 1.0f;
         final float targetCenterX = DEFAULT_CENTER_X + 100;
         final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -277,7 +367,6 @@
         verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
         verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
         verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
-        verify(mAnimationCallback).onResult(false);
         verify(mAnimationCallback2).onResult(true);
     }
 
@@ -295,7 +384,7 @@
 
     @Test
     public void setScale_enabled_expectedScale() {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+        enableWindowMagnificationWithoutAnimation();
 
         mInstrumentation.runOnMainSync(
                 () -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1));
@@ -307,13 +396,12 @@
     @Test
     public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()
             throws RemoteException {
-        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+        enableWindowMagnificationWithoutAnimation();
 
         deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
 
         verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture());
-        verify(mSpyController).deleteWindowMagnification();
         verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
         verifyStartValue(mCenterXCaptor, Float.NaN);
         verifyStartValue(mCenterYCaptor, Float.NaN);
@@ -322,6 +410,15 @@
     }
 
     @Test
+    public void deleteWindowMagnificationWithoutCallback_enabled_expectedValues() {
+        enableWindowMagnificationWithoutAnimation();
+
+        deleteWindowMagnificationAndWaitAnimating(0, null);
+
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+    }
+
+    @Test
     public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback()
             throws RemoteException {
         deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
@@ -346,10 +443,8 @@
                     mCurrentCenterY.set(mController.getCenterY());
                 });
         SystemClock.sleep(mWaitingAnimationPeriod);
-
         verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture());
-        verify(mSpyController).deleteWindowMagnification();
 
         //The animation is in verse, so we only check the start values should no be greater than
         // the current one.
@@ -363,6 +458,22 @@
     }
 
     @Test
+    public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback()
+            throws RemoteException {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationCallback);
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.deleteWindowMagnification(null);
+                });
+
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+        verify(mAnimationCallback).onResult(false);
+    }
+
+    @Test
     public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException {
         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
         deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
@@ -372,7 +483,6 @@
 
         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(mAnimationCallback).onResult(false);
@@ -380,8 +490,22 @@
     }
 
     @Test
-    public void moveWindowMagnifier_enabled() {
+    public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues()
+            throws RemoteException {
         enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+                mAnimationCallback);
+
+        deleteWindowMagnificationAndWaitAnimating(0, null);
+
+        verify(mSpyController).deleteWindowMagnification();
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+        verify(mAnimationCallback).onResult(false);
+    }
+
+    @Test
+    public void moveWindowMagnifier_enabled() {
+        enableWindowMagnificationWithoutAnimation();
 
         mInstrumentation.runOnMainSync(
                 () -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f));
@@ -411,6 +535,15 @@
         assertEquals(expectedCenterY, mController.getCenterY(), 0f);
     }
 
+    private void enableWindowMagnificationWithoutAnimation() {
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+                            DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null);
+                });
+    }
+
     private void enableWindowMagnificationAndWaitAnimating(long duration,
             @Nullable IRemoteMagnificationAnimationCallback callback) {
         mInstrumentation.runOnMainSync(
@@ -485,11 +618,16 @@
         }
 
         @Override
+        public void updateSysUIStateFlag() {
+            super.updateSysUIStateFlag();
+            mSpyController.updateSysUIStateFlag();
+        }
+
+        @Override
         void onConfigurationChanged(int configDiff) {
             super.onConfigurationChanged(configDiff);
             mSpyController.onConfigurationChanged(configDiff);
         }
-
     }
 
     private static ValueAnimator newValueAnimator() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 14a60be..70457cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -469,6 +469,21 @@
     }
 
     @Test
+    public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(0.9f, Float.NaN,
+                    Float.NaN);
+        });
+
+        assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0);
+    }
+
+    @Test
     public void onLocaleChanged_enabled_updateA11yWindowTitle() {
         final String newA11yWindowTitle = "new a11y window title";
         mInstrumentation.runOnMainSync(() -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
index 2fa32ba..6347638 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
@@ -53,8 +53,6 @@
 public class FeatureFlagManagerTest extends SysuiTestCase {
     FeatureFlagManager mFeatureFlagManager;
 
-    @Mock private FlagManager mFlagManager;
-    @Mock private SecureSettings mSecureSettings;
     @Mock private Context mContext;
     @Mock private DumpManager mDumpManager;
 
@@ -62,14 +60,11 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        mFeatureFlagManager = new FeatureFlagManager(mSecureSettings, mContext, mDumpManager);
+        mFeatureFlagManager = new FeatureFlagManager(mDumpManager);
     }
 
     @After
     public void onFinished() {
-        // SecureSettings and Context are provided for constructor consistency with the
-        // debug version of the FeatureFlagManager, but should never be used.
-        verifyZeroInteractions(mSecureSettings, mContext);
         // The dump manager should be registered with even for the release version, but that's it.
         verify(mDumpManager).registerDumpable(anyString(), any());
         verifyNoMoreInteractions(mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
deleted file mode 100644
index 30e9b51..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2021 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.flags;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-@SmallTest
-public class FeatureFlagsTest extends SysuiTestCase {
-
-    @Mock Resources mResources;
-    @Mock FlagReader mFeatureFlagReader;
-
-    private FeatureFlags mFeatureFlags;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mFeatureFlagReader.isEnabled(anyInt(), anyBoolean())).thenAnswer(
-                (Answer<Boolean>) invocation -> invocation.getArgument(1));
-
-        mFeatureFlags = new FeatureFlags(mResources, mFeatureFlagReader, getContext());
-    }
-
-    @Test
-    public void testAddListener() {
-        Flag<?> flag = new BooleanFlag(1);
-        mFeatureFlags.addFlag(flag);
-
-        // Assert and capture that a plugin listener was added.
-        ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
-                ArgumentCaptor.forClass(FlagReader.Listener.class);
-        verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
-        FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
-        // Signal a change. No listeners, so no real effect.
-        pluginListener.onFlagChanged(flag.getId());
-
-        // Add a listener for the flag
-        final Flag<?>[] changedFlag = {null};
-        FeatureFlags.Listener listener = f -> changedFlag[0] = f;
-        mFeatureFlags.addFlagListener(flag, listener);
-
-        // No changes seen yet.
-        assertThat(changedFlag[0]).isNull();
-
-        // Signal a change.
-        pluginListener.onFlagChanged(flag.getId());
-
-        // Assert that the change was for the correct flag.
-        assertThat(changedFlag[0]).isEqualTo(flag);
-    }
-
-    @Test
-    public void testRemoveListener() {
-        Flag<?> flag = new BooleanFlag(1);
-        mFeatureFlags.addFlag(flag);
-
-        // Assert and capture that a plugin listener was added.
-        ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
-                ArgumentCaptor.forClass(FlagReader.Listener.class);
-        verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
-        FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
-        // Add a listener for the flag
-        final Flag<?>[] changedFlag = {null};
-        FeatureFlags.Listener listener = f -> changedFlag[0] = f;
-        mFeatureFlags.addFlagListener(flag, listener);
-
-        // Signal a change.
-        pluginListener.onFlagChanged(flag.getId());
-
-        // Assert that the change was for the correct flag.
-        assertThat(changedFlag[0]).isEqualTo(flag);
-
-        changedFlag[0] = null;
-
-        // Now remove the listener.
-        mFeatureFlags.removeFlagListener(flag, listener);
-        // Signal a change.
-        pluginListener.onFlagChanged(flag.getId());
-        // Assert that the change was not triggered
-        assertThat(changedFlag[0]).isNull();
-    }
-
-    @Test
-    public void testBooleanDefault() {
-        BooleanFlag flag = new BooleanFlag(1, true);
-
-        mFeatureFlags.addFlag(flag);
-
-        assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
-    }
-
-    @Test
-    public void testBooleanResourceOverlay() {
-        int resourceId = 12;
-        BooleanFlag flag = new BooleanFlag(1, false, resourceId);
-        when(mResources.getBoolean(resourceId)).thenReturn(true);
-        when(mResources.getResourceEntryName(resourceId)).thenReturn("flag");
-
-        mFeatureFlags.addFlag(flag);
-
-        assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
index 8ff33a0..3c24a3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
@@ -16,22 +16,16 @@
 
 package com.android.systemui.idle;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.BroadcastReceiver;
-import android.content.Intent;
 import android.content.res.Resources;
-import android.os.Looper;
 import android.os.PowerManager;
 import android.testing.AndroidTestingRunner;
-import android.view.Choreographer;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
@@ -40,10 +34,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -60,22 +51,14 @@
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
     @Mock private PowerManager mPowerManager;
     @Mock private IdleHostView mIdleHostView;
-    @Mock private InputMonitorFactory mInputMonitorFactory;
-    @Mock private DelayableExecutor mDelayableExecutor;
     @Mock private Resources mResources;
-    @Mock private Looper mLooper;
     @Mock private Provider<View> mViewProvider;
-    @Mock private Choreographer mChoreographer;
     @Mock private KeyguardStateController mKeyguardStateController;
     @Mock private StatusBarStateController mStatusBarStateController;
-    @Mock private DreamHelper mDreamHelper;
-    @Mock private InputMonitorCompat mInputMonitor;
-    @Mock private InputChannelCompat.InputEventReceiver mInputEventReceiver;
     @Mock private AmbientLightModeMonitor mAmbientLightModeMonitor;
 
     private KeyguardStateController.Callback mKeyguardStateCallback;
     private StatusBarStateController.StateListener mStatusBarStateListener;
-    private IdleHostViewController mController;
 
     @Before
     public void setup() {
@@ -83,17 +66,12 @@
 
         when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true);
         when(mStatusBarStateController.isDozing()).thenReturn(false);
-        when(mInputMonitorFactory.getInputMonitor("IdleHostViewController"))
-                .thenReturn(mInputMonitor);
-        when(mInputMonitor.getInputReceiver(any(), any(), any())).thenReturn(mInputEventReceiver);
 
-        mController = new IdleHostViewController(mContext,
-                mBroadcastDispatcher, mPowerManager, mIdleHostView,
-                mInputMonitorFactory, mDelayableExecutor, mResources, mLooper, mViewProvider,
-                mChoreographer, mKeyguardStateController, mStatusBarStateController, mDreamHelper,
-                mAmbientLightModeMonitor);
-        mController.init();
-        mController.onViewAttached();
+        final IdleHostViewController controller = new IdleHostViewController(mBroadcastDispatcher,
+                mPowerManager, mIdleHostView, mResources, mViewProvider, mKeyguardStateController,
+                mStatusBarStateController, mAmbientLightModeMonitor);
+        controller.init();
+        controller.onViewAttached();
 
         // Captures keyguard state controller callback.
         ArgumentCaptor<KeyguardStateController.Callback> keyguardStateCallbackCaptor =
@@ -109,101 +87,6 @@
     }
 
     @Test
-    public void testTimeoutToIdleMode() {
-        // Keyguard showing.
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
-
-        // Regular ambient lighting.
-        final AmbientLightModeMonitor.Callback lightMonitorCallback =
-                captureAmbientLightModeMonitorCallback();
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
-        // Times out.
-        ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
-        verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong());
-        callbackCapture.getValue().run();
-
-        // Verifies start dreaming (idle mode).
-        verify(mDreamHelper).startDreaming(any());
-    }
-
-    @Test
-    public void testTimeoutToLowLightMode() {
-        // Keyguard showing.
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
-
-        // Captures dream broadcast receiver;
-        ArgumentCaptor<BroadcastReceiver> dreamBroadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mBroadcastDispatcher)
-                .registerReceiver(dreamBroadcastReceiverCaptor.capture(), any());
-        final BroadcastReceiver dreamBroadcastReceiver = dreamBroadcastReceiverCaptor.getValue();
-
-        // Low ambient lighting.
-        final AmbientLightModeMonitor.Callback lightMonitorCallback =
-                captureAmbientLightModeMonitorCallback();
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-
-        // Verifies it goes to sleep because of low light.
-        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-
-        mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
-        dreamBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STARTED));
-
-        // User wakes up the device.
-        mStatusBarStateListener.onDozingChanged(false /*isDozing*/);
-        dreamBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
-
-        // Clears power manager invocations to make sure the below dozing was triggered by the
-        // timeout.
-        clearInvocations(mPowerManager);
-
-        // Times out.
-        ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
-        verify(mDelayableExecutor, atLeastOnce()).executeDelayed(callbackCapture.capture(),
-                anyLong());
-        callbackCapture.getValue().run();
-
-        // Verifies go to sleep (low light mode).
-        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-    }
-
-    @Test
-    public void testTransitionBetweenIdleAndLowLightMode() {
-        // Keyguard showing.
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
-
-        // Regular ambient lighting.
-        final AmbientLightModeMonitor.Callback lightMonitorCallback =
-                captureAmbientLightModeMonitorCallback();
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
-        // Times out.
-        ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
-        verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong());
-        callbackCapture.getValue().run();
-
-        // Verifies in idle mode (dreaming).
-        verify(mDreamHelper).startDreaming(any());
-        clearInvocations(mDreamHelper);
-
-        // Ambient lighting becomes dim.
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-
-        // Verifies in low light mode (dozing).
-        verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-
-        // Ambient lighting becomes bright again.
-        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
-        // Verifies in idle mode (dreaming).
-        verify(mDreamHelper).startDreaming(any());
-    }
-
-    @Test
     public void testStartDozingWhenLowLight() {
         // Keyguard showing.
         when(mKeyguardStateController.isShowing()).thenReturn(true);
@@ -225,20 +108,22 @@
     }
 
     @Test
-    public void testInputEventReceiverLifecycle() {
+    public void testWakeUpWhenRegularLight() {
         // Keyguard showing.
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         mKeyguardStateCallback.onKeyguardShowingChanged();
 
-        // Should register input event receiver.
-        verify(mInputMonitor).getInputReceiver(any(), any(), any());
+        // In low light / dozing.
+        final AmbientLightModeMonitor.Callback lightMonitorCallback =
+                captureAmbientLightModeMonitorCallback();
+        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+        mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
 
-        // Keyguard dismissed.
-        when(mKeyguardStateController.isShowing()).thenReturn(false);
-        mKeyguardStateCallback.onKeyguardShowingChanged();
+        // Regular ambient lighting.
+        lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
 
-        // Should dispose input event receiver.
-        verify(mInputEventReceiver).dispose();
+        // Verifies it wakes up from sleep.
+        verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
     }
 
     // Captures [AmbientLightModeMonitor.Callback] assuming that the ambient light mode monitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
index df11284..5a4bb86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
@@ -40,7 +40,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import org.junit.After;
@@ -69,8 +68,6 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
-    private KeyguardBypassController mBypassController;
-    @Mock
     private Resources mResources;
 
     private MockitoSession mStaticMockSession;
@@ -99,7 +96,6 @@
                 mBroadcastDispatcher,
                 mBatteryController,
                 mKeyguardUpdateMonitor,
-                mBypassController,
                 mResources
         );
         mAnimatableClockController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 5e73dbc..d64319b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -212,6 +212,7 @@
     @Test
     public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() {
         // GIVEN fp sensor location is not available pre-init
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
         when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
         when(mAuthController.getUdfpsProps()).thenReturn(null);
         mLockIconViewController.init();
@@ -232,7 +233,7 @@
     }
 
     @Test
-    public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() {
+    public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
         // GIVEN Udpfs sensor location is available
         setupUdfps();
 
@@ -247,9 +248,9 @@
     }
 
     @Test
-    public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() {
-        // GIVEN Udfps sensor location is not available
-        when(mAuthController.getUdfpsSensorLocation()).thenReturn(null);
+    public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
+        // GIVEN Udfps sensor location is not supported
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
 
         mLockIconViewController.init();
         captureAttachListener();
@@ -365,6 +366,7 @@
     }
 
     private Pair<Integer, PointF> setupUdfps() {
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
         final PointF udfpsLocation = new PointF(50, 75);
         final int radius = 33;
         final FingerprintSensorPropertiesInternal fpProps =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index af624ed..5de4c11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -182,7 +182,7 @@
 
     @Test
     public void testShowImeButton() {
-        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true, false);
+        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
         waitForIdleSync();
         verify(mCallbacks).setImeWindowStatus(
                 eq(DEFAULT_DISPLAY), eq(null), eq(1), eq(2), eq(true));
@@ -193,7 +193,7 @@
         // First show in default display to update the "last updated ime display"
         testShowImeButton();
 
-        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true, false);
+        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true);
         waitForIdleSync();
         verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE),
                 eq(BACK_DISPOSITION_DEFAULT), eq(false));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 01f7fae..cb0d87a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -701,7 +701,7 @@
 
         // GIVEN fingerprint is also running (not udfps)
         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
 
         mController.setVisible(true);
 
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 cf58c63..9a23eb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -44,7 +44,7 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 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.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;
@@ -103,7 +103,6 @@
         when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true);
         when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true);
 
-        when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
         when(mFeatureFlags.checkLegacyPipelineEnabled()).thenReturn(true);
 
         mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
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 902d115..f8effa1 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
@@ -192,7 +192,6 @@
         mEntry = createNotification();
         mSbn = mEntry.getSbn();
 
-        when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mEntryManager = new NotificationEntryManager(
                 mLogger,
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 f08a74a..cf90cef6 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
@@ -145,7 +145,6 @@
         allowTestableLooperAsMainThread();
 
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
-        when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(true);
 
         when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index fc94262..b91f7e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -30,6 +30,7 @@
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
 import com.android.systemui.util.time.FakeSystemClock;
 
 import java.util.ArrayList;
@@ -53,6 +54,7 @@
 
     /* ListEntry properties */
     private GroupEntry mParent;
+    private NotifSection mNotifSection;
 
     /* If set, use this creation time instead of mClock.uptimeMillis */
     private long mCreationTime = -1;
@@ -71,6 +73,11 @@
         mCreationTime = source.getCreationTime();
     }
 
+    /** Update an the parent on an existing entry */
+    public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
+        entry.setParent(parent);
+    }
+
     /** Build a new instance of NotificationEntry */
     public NotificationEntry build() {
         return buildOrApply(null);
@@ -103,6 +110,7 @@
 
         /* ListEntry properties */
         entry.setParent(mParent);
+        entry.getAttachState().setSection(mNotifSection);
         entry.getAttachState().setStableIndex(mStableIndex);
         return entry;
     }
@@ -116,6 +124,14 @@
     }
 
     /**
+     * Sets the parent.
+     */
+    public NotificationEntryBuilder setSection(@Nullable NotifSection section) {
+        mNotifSection = section;
+        return this;
+    }
+
+    /**
      * Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be
      * ignored.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 913ffd4..b254ed4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpTree;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -44,7 +46,7 @@
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -79,10 +81,11 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
 @SmallTest
@@ -124,7 +127,7 @@
 
         mListBuilder.attach(mNotifCollection);
 
-        mStabilityManager = new TestableStabilityManager();
+        mStabilityManager = spy(new TestableStabilityManager());
         mListBuilder.setNotifStabilityManager(mStabilityManager);
 
         Mockito.verify(mNotifCollection).setBuildListener(mBuildListenerCaptor.capture());
@@ -618,26 +621,53 @@
 
     @Test
     public void testNotifSectionsChildrenUpdated() {
-        AtomicBoolean validChildren = new AtomicBoolean(false);
+        ArrayList<ListEntry> pkg1Entries = new ArrayList<>();
+        ArrayList<ListEntry> pkg2Entries = new ArrayList<>();
+        ArrayList<ListEntry> pkg3Entries = new ArrayList<>();
         final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1) {
-            @Nullable
             @Override
             public void onEntriesUpdated(List<ListEntry> entries) {
                 super.onEntriesUpdated(entries);
-                validChildren.set(entries.size() == 2);
+                pkg1Entries.addAll(entries);
             }
         });
-        mListBuilder.setSectioners(asList(pkg1Sectioner));
+        final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2) {
+            @Override
+            public void onEntriesUpdated(List<ListEntry> entries) {
+                super.onEntriesUpdated(entries);
+                pkg2Entries.addAll(entries);
+            }
+        });
+        final NotifSectioner pkg3Sectioner = spy(new PackageSectioner(PACKAGE_3) {
+            @Override
+            public void onEntriesUpdated(List<ListEntry> entries) {
+                super.onEntriesUpdated(entries);
+                pkg3Entries.addAll(entries);
+            }
+        });
+        mListBuilder.setSectioners(asList(pkg1Sectioner, pkg2Sectioner, pkg3Sectioner));
 
-        addNotif(0, PACKAGE_4);
+        addNotif(0, PACKAGE_1);
         addNotif(1, PACKAGE_1);
-        addNotif(2, PACKAGE_1);
+        addNotif(2, PACKAGE_3);
         addNotif(3, PACKAGE_3);
+        addNotif(4, PACKAGE_3);
 
         dispatchBuild();
 
-        verify(pkg1Sectioner, times(1)).onEntriesUpdated(any());
-        assertTrue(validChildren.get());
+        verify(pkg1Sectioner).onEntriesUpdated(any());
+        verify(pkg2Sectioner).onEntriesUpdated(any());
+        verify(pkg3Sectioner).onEntriesUpdated(any());
+        assertThat(pkg1Entries).containsExactly(
+                mEntrySet.get(0),
+                mEntrySet.get(1)
+        ).inOrder();
+        assertThat(pkg2Entries).isEmpty();
+        assertThat(pkg3Entries).containsExactly(
+                mEntrySet.get(2),
+                mEntrySet.get(3),
+                mEntrySet.get(4)
+        ).inOrder();
     }
 
     @Test
@@ -835,13 +865,13 @@
                 .onBeforeTransformGroups(anyList());
         inOrder.verify(promoter, atLeastOnce())
                 .shouldPromoteToTopLevel(any(NotificationEntry.class));
-        inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
-        inOrder.verify(preRenderFilter, atLeastOnce())
-                .shouldFilterOut(any(NotificationEntry.class), anyLong());
         inOrder.verify(mOnBeforeSortListener).onBeforeSort(anyList());
         inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class));
         inOrder.verify(comparator, atLeastOnce())
                 .compare(any(ListEntry.class), any(ListEntry.class));
+        inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
+        inOrder.verify(preRenderFilter, atLeastOnce())
+                .shouldFilterOut(any(NotificationEntry.class), anyLong());
         inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
         inOrder.verify(mOnRenderListListener).onRenderList(anyList());
     }
@@ -947,13 +977,11 @@
         );
 
         // THEN all the new notifs, including the new GroupEntry, are passed to the listener
-        assertEquals(
-                asList(
-                        mEntrySet.get(0),
-                        mBuiltList.get(1),
-                        mEntrySet.get(4)),
-                listener.mEntriesReceived
-        );
+        assertThat(listener.mEntriesReceived).containsExactly(
+                mEntrySet.get(0),
+                mBuiltList.get(1),
+                mEntrySet.get(4)
+        ).inOrder(); // Order is a bonus because this listener is before sort
     }
 
     @Test
@@ -993,14 +1021,12 @@
         );
 
         // THEN all the new notifs, including the new GroupEntry, are passed to the listener
-        assertEquals(
-                asList(
-                        mEntrySet.get(0),
-                        mBuiltList.get(2),
-                        mEntrySet.get(7),
-                        mEntrySet.get(1)),
-                listener.mEntriesReceived
-        );
+        assertThat(listener.mEntriesReceived).containsExactly(
+                mEntrySet.get(0),
+                mEntrySet.get(1),
+                mBuiltList.get(2),
+                mEntrySet.get(7)
+        ).inOrder(); // Order is a bonus because this listener is before sort
     }
 
     @Test
@@ -1091,9 +1117,93 @@
     }
 
     @Test
+    public void testFinalizeFilteringGroupSummaryDoesNotBreakSort() {
+        // GIVEN children from 3 packages, with one in the middle of the sort order being a group
+        addNotif(0, PACKAGE_1);
+        addNotif(1, PACKAGE_2);
+        addNotif(2, PACKAGE_3);
+        addNotif(3, PACKAGE_1);
+        addNotif(4, PACKAGE_2);
+        addNotif(5, PACKAGE_3);
+        addGroupSummary(6, PACKAGE_2, GROUP_1);
+        addGroupChild(7, PACKAGE_2, GROUP_1);
+        addGroupChild(8, PACKAGE_2, GROUP_1);
+
+        // GIVEN that they should be sorted by package
+        mListBuilder.setComparators(asList(
+                new HypeComparator(PACKAGE_1),
+                new HypeComparator(PACKAGE_2),
+                new HypeComparator(PACKAGE_3)
+        ));
+
+        // WHEN a finalize filter removes the summary
+        mListBuilder.addFinalizeFilter(new NotifFilter("Test") {
+            @Override
+            public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+                return entry == notif(6).entry;
+            }
+        });
+
+        dispatchBuild();
+
+        // THEN the notifications remain ordered by package, even though the children were promoted
+        verifyBuiltList(
+                notif(0),
+                notif(3),
+                notif(1),
+                notif(4),
+                notif(7),  // promoted child
+                notif(8),  // promoted child
+                notif(2),
+                notif(5)
+        );
+    }
+
+    @Test
+    public void testFinalizeFilteringGroupChildDoesNotBreakSort() {
+        // GIVEN children from 3 packages, with one in the middle of the sort order being a group
+        addNotif(0, PACKAGE_1);
+        addNotif(1, PACKAGE_2);
+        addNotif(2, PACKAGE_3);
+        addNotif(3, PACKAGE_1);
+        addNotif(4, PACKAGE_2);
+        addNotif(5, PACKAGE_3);
+        addGroupSummary(6, PACKAGE_2, GROUP_1);
+        addGroupChild(7, PACKAGE_2, GROUP_1);
+        addGroupChild(8, PACKAGE_2, GROUP_1);
+
+        // GIVEN that they should be sorted by package
+        mListBuilder.setComparators(asList(
+                new HypeComparator(PACKAGE_1),
+                new HypeComparator(PACKAGE_2),
+                new HypeComparator(PACKAGE_3)
+        ));
+
+        // WHEN a finalize filter one of the 2 children from a group
+        mListBuilder.addFinalizeFilter(new NotifFilter("Test") {
+            @Override
+            public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+                return entry == notif(7).entry;
+            }
+        });
+
+        dispatchBuild();
+
+        // THEN the notifications remain ordered by package, even though the children were promoted
+        verifyBuiltList(
+                notif(0),
+                notif(3),
+                notif(1),
+                notif(4),
+                notif(8),  // promoted child
+                notif(2),
+                notif(5)
+        );
+    }
+
+    @Test
     public void testBrokenGroupNotificationOrdering() {
         // GIVEN two group children with different sections & without a summary yet
-
         addGroupChild(0, PACKAGE_2, GROUP_1);
         addNotif(1, PACKAGE_1);
         addGroupChild(2, PACKAGE_2, GROUP_1);
@@ -1224,13 +1334,11 @@
         dispatchBuild();
 
         // THEN all the new notifs are passed to the listener out of order
-        assertEquals(
-                asList(
-                        mEntrySet.get(0),
-                        mEntrySet.get(1),
-                        mEntrySet.get(2)),
-                listener.mEntriesReceived
-        );
+        assertThat(listener.mEntriesReceived).containsExactly(
+                mEntrySet.get(0),
+                mEntrySet.get(1),
+                mEntrySet.get(2)
+        ).inOrder();  // Checking out-of-order input to validate sorted output
 
         // THEN the final list is in order
         verifyBuiltList(
@@ -1256,13 +1364,11 @@
         dispatchBuild();
 
         // THEN all the new notifs are passed to the listener
-        assertEquals(
-                asList(
-                        mEntrySet.get(0),
-                        mEntrySet.get(1),
-                        mEntrySet.get(2)),
-                listener.mEntriesReceived
-        );
+        assertThat(listener.mEntriesReceived).containsExactly(
+                mEntrySet.get(0),
+                mEntrySet.get(1),
+                mEntrySet.get(2)
+        ).inOrder();
     }
 
     @Test
@@ -1365,6 +1471,7 @@
         assertOrder("ABCDEFG", "ACDEFBG", "ABCDEFG"); // no change
         assertOrder("ABCDEFG", "ACDEFBXZG", "XZABCDEFG"); // Z and X
         assertOrder("ABCDEFG", "AXCDEZFBG", "XZABCDEFG"); // Z and X + gap
+        verify(mStabilityManager, times(4)).onEntryReorderSuppressed();
     }
 
     @Test
@@ -1373,6 +1480,7 @@
         assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG"); // no change
         assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG"); // Z and X
         assertOrder("ABCDEFG", "AXCDEZFBG", "AXCDEZFBG"); // Z and X + gap
+        verify(mStabilityManager, never()).onEntryReorderSuppressed();
     }
 
     @Test
@@ -1410,6 +1518,26 @@
         // THEN no exception thrown
     }
 
+    @Test
+    public void testIsSorted() {
+        Comparator<Integer> intCmp = Integer::compare;
+        assertTrue(ShadeListBuilder.isSorted(Collections.emptyList(), intCmp));
+        assertTrue(ShadeListBuilder.isSorted(Collections.singletonList(1), intCmp));
+        assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2), intCmp));
+        assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3), intCmp));
+        assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4), intCmp));
+        assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4, 5), intCmp));
+        assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 1, 1, 1, 1), intCmp));
+        assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 1, 2, 2, 3, 3), intCmp));
+
+        assertFalse(ShadeListBuilder.isSorted(Arrays.asList(2, 1), intCmp));
+        assertFalse(ShadeListBuilder.isSorted(Arrays.asList(2, 1, 2), intCmp));
+        assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 1), intCmp));
+        assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 2, 5), intCmp));
+        assertFalse(ShadeListBuilder.isSorted(Arrays.asList(5, 2, 3, 4, 5), intCmp));
+        assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4, 1), intCmp));
+    }
+
     /**
      * Adds a notif to the collection that will be passed to the list builder when
      * {@link #dispatchBuild()}s is called.
@@ -1815,6 +1943,15 @@
         public boolean isEntryReorderingAllowed(ListEntry entry) {
             return mAllowEntryReodering;
         }
+
+        @Override
+        public boolean isEveryChangeAllowed() {
+            return mAllowEntryReodering && mAllowGroupChanges && mAllowSectionChanges;
+        }
+
+        @Override
+        public void onEntryReorderSuppressed() {
+        }
     }
 
     private static final String PACKAGE_1 = "com.test1";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index bec5174..c3e10aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -26,6 +26,7 @@
 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 static java.util.Objects.requireNonNull;
 
@@ -33,10 +34,12 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -44,8 +47,11 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
 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.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn;
 import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
@@ -60,6 +66,7 @@
 import org.mockito.Spy;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -79,24 +86,36 @@
     @Captor private ArgumentCaptor<NotifCollectionListener> mCollectionListenerCaptor;
     @Captor private ArgumentCaptor<OnBeforeFinalizeFilterListener> mBeforeFilterListenerCaptor;
     @Captor private ArgumentCaptor<NotifInflater.InflationCallback> mCallbackCaptor;
+    @Captor private ArgumentCaptor<NotifInflater.Params> mParamsCaptor;
 
+    @Mock private NotifSectioner mNotifSectioner;
+    @Mock private NotifSection mNotifSection;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private IStatusBarService mService;
     @Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
+    private final TestableAdjustmentProvider mAdjustmentProvider = new TestableAdjustmentProvider();
+
+    @NonNull
+    private NotificationEntryBuilder getNotificationEntryBuilder() {
+        return new NotificationEntryBuilder().setSection(mNotifSection);
+    }
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mEntry = new NotificationEntryBuilder().setParent(ROOT_ENTRY).build();
+        mEntry = getNotificationEntryBuilder().setParent(ROOT_ENTRY).build();
         mInflationError = new Exception(TEST_MESSAGE);
         mErrorManager = new NotifInflationErrorManager();
+        when(mNotifSection.getSectioner()).thenReturn(mNotifSectioner);
+        mAdjustmentProvider.setSectionIsLowPriority(false);
 
         PreparationCoordinator coordinator = new PreparationCoordinator(
                 mock(PreparationCoordinatorLogger.class),
                 mNotifInflater,
                 mErrorManager,
                 mock(NotifViewBarn.class),
+                mAdjustmentProvider,
                 mService,
                 TEST_CHILD_BIND_CUTOFF,
                 TEST_MAX_GROUP_DELAY);
@@ -150,7 +169,7 @@
         mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
 
         // THEN we inflate it
-        verify(mNotifInflater).inflateViews(eq(mEntry), any());
+        verify(mNotifInflater).inflateViews(eq(mEntry), any(), any());
 
         // THEN we filter it out until it's done inflating.
         assertTrue(mUninflatedFilter.shouldFilterOut(mEntry, 0));
@@ -161,7 +180,7 @@
         // GIVEN an inflated notification
         mCollectionListener.onEntryAdded(mEntry);
         mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
-        verify(mNotifInflater).inflateViews(eq(mEntry), mCallbackCaptor.capture());
+        verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
         mCallbackCaptor.getValue().onInflationFinished(mEntry);
 
         // WHEN notification is updated
@@ -169,7 +188,90 @@
         mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
 
         // THEN we rebind it
-        verify(mNotifInflater).rebindViews(eq(mEntry), any());
+        verify(mNotifInflater).rebindViews(eq(mEntry), any(), any());
+
+        // THEN we do not filter it because it's not the first inflation.
+        assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testEntrySmartReplyAdditionWillRebindViews() {
+        // GIVEN an inflated notification
+        mCollectionListener.onEntryAdded(mEntry);
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+        verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+        // WHEN notification ranking now has smart replies
+        mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setSmartReplies("yes").build());
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+        // THEN we rebind it
+        verify(mNotifInflater).rebindViews(eq(mEntry), any(), any());
+
+        // THEN we do not filter it because it's not the first inflation.
+        assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testEntryChangedToMinimizedSectionWillRebindViews() {
+        // GIVEN an inflated notification
+        mCollectionListener.onEntryAdded(mEntry);
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+        verify(mNotifInflater).inflateViews(eq(mEntry),
+                mParamsCaptor.capture(), mCallbackCaptor.capture());
+        assertFalse(mParamsCaptor.getValue().isLowPriority());
+        mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+        // WHEN notification moves to a min priority section
+        mAdjustmentProvider.setSectionIsLowPriority(true);
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+        // THEN we rebind it
+        verify(mNotifInflater).rebindViews(eq(mEntry), mParamsCaptor.capture(), any());
+        assertTrue(mParamsCaptor.getValue().isLowPriority());
+
+        // THEN we do not filter it because it's not the first inflation.
+        assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testMinimizedEntryMovedIntoGroupWillRebindViews() {
+        // GIVEN an inflated, minimized notification
+        mAdjustmentProvider.setSectionIsLowPriority(true);
+        mCollectionListener.onEntryAdded(mEntry);
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+        verify(mNotifInflater).inflateViews(eq(mEntry),
+                mParamsCaptor.capture(), mCallbackCaptor.capture());
+        assertTrue(mParamsCaptor.getValue().isLowPriority());
+        mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+        // WHEN notification is moved under a parent
+        NotificationEntryBuilder.setNewParent(mEntry, mock(GroupEntry.class));
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+        // THEN we rebind it as not-minimized
+        verify(mNotifInflater).rebindViews(eq(mEntry), mParamsCaptor.capture(), any());
+        assertFalse(mParamsCaptor.getValue().isLowPriority());
+
+        // THEN we do not filter it because it's not the first inflation.
+        assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testEntryRankChangeWillNotRebindViews() {
+        // GIVEN an inflated notification
+        mCollectionListener.onEntryAdded(mEntry);
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+        verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
+        mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+        // WHEN notification ranking changes rank, which does not affect views
+        mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setRank(100).build());
+        mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+        // THEN we do not rebind it
+        verify(mNotifInflater, never()).rebindViews(eq(mEntry), any(), any());
 
         // THEN we do not filter it because it's not the first inflation.
         assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
@@ -180,7 +282,7 @@
         // GIVEN an inflated notification
         mCollectionListener.onEntryAdded(mEntry);
         mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
-        verify(mNotifInflater).inflateViews(eq(mEntry), mCallbackCaptor.capture());
+        verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
         mCallbackCaptor.getValue().onInflationFinished(mEntry);
 
         // THEN it isn't filtered from shade list
@@ -191,13 +293,13 @@
     public void testCutoffGroupChildrenNotInflated() {
         // WHEN there is a new notification group is posted
         int id = 0;
-        NotificationEntry summary = new NotificationEntryBuilder()
+        NotificationEntry summary = getNotificationEntryBuilder()
                 .setOverrideGroupKey(TEST_GROUP_KEY)
                 .setId(id++)
                 .build();
         List<NotificationEntry> children = new ArrayList<>();
         for (int i = 0; i < TEST_CHILD_BIND_CUTOFF + 1; i++) {
-            NotificationEntry child = new NotificationEntryBuilder()
+            NotificationEntry child = getNotificationEntryBuilder()
                     .setOverrideGroupKey(TEST_GROUP_KEY)
                     .setId(id++)
                     .build();
@@ -224,9 +326,9 @@
         // THEN we inflate up to the cut-off only
         for (int i = 0; i < children.size(); i++) {
             if (i < TEST_CHILD_BIND_CUTOFF) {
-                verify(mNotifInflater).inflateViews(eq(children.get(i)), any());
+                verify(mNotifInflater).inflateViews(eq(children.get(i)), any(), any());
             } else {
-                verify(mNotifInflater, never()).inflateViews(eq(children.get(i)), any());
+                verify(mNotifInflater, never()).inflateViews(eq(children.get(i)), any(), any());
             }
         }
     }
@@ -236,9 +338,9 @@
         // GIVEN a newly-posted group with a summary and two children
         final GroupEntry group = new GroupEntryBuilder()
                 .setCreationTime(400)
-                .setSummary(new NotificationEntryBuilder().setId(1).build())
-                .addChild(new NotificationEntryBuilder().setId(2).build())
-                .addChild(new NotificationEntryBuilder().setId(3).build())
+                .setSummary(getNotificationEntryBuilder().setId(1).build())
+                .addChild(getNotificationEntryBuilder().setId(2).build())
+                .addChild(getNotificationEntryBuilder().setId(3).build())
                 .build();
         fireAddEvents(List.of(group));
         final NotificationEntry child0 = group.getChildren().get(0);
@@ -256,9 +358,9 @@
         // GIVEN a newly-posted group with a summary and two children
         final GroupEntry group = new GroupEntryBuilder()
                 .setCreationTime(400)
-                .setSummary(new NotificationEntryBuilder().setId(1).build())
-                .addChild(new NotificationEntryBuilder().setId(2).build())
-                .addChild(new NotificationEntryBuilder().setId(3).build())
+                .setSummary(getNotificationEntryBuilder().setId(1).build())
+                .addChild(getNotificationEntryBuilder().setId(2).build())
+                .addChild(getNotificationEntryBuilder().setId(3).build())
                 .build();
         fireAddEvents(List.of(group));
         final NotificationEntry summary = group.getSummary();
@@ -281,9 +383,9 @@
         // GIVEN a newly-posted group with a summary and two children
         final GroupEntry group = new GroupEntryBuilder()
                 .setCreationTime(400)
-                .setSummary(new NotificationEntryBuilder().setId(1).build())
-                .addChild(new NotificationEntryBuilder().setId(2).build())
-                .addChild(new NotificationEntryBuilder().setId(3).build())
+                .setSummary(getNotificationEntryBuilder().setId(1).build())
+                .addChild(getNotificationEntryBuilder().setId(2).build())
+                .addChild(getNotificationEntryBuilder().setId(3).build())
                 .build();
         fireAddEvents(List.of(group));
         final NotificationEntry summary = group.getSummary();
@@ -307,9 +409,9 @@
         // GIVEN a newly-posted group with a summary and two children
         final GroupEntry group = new GroupEntryBuilder()
                 .setCreationTime(400)
-                .setSummary(new NotificationEntryBuilder().setId(1).build())
-                .addChild(new NotificationEntryBuilder().setId(2).build())
-                .addChild(new NotificationEntryBuilder().setId(3).build())
+                .setSummary(getNotificationEntryBuilder().setId(1).build())
+                .addChild(getNotificationEntryBuilder().setId(2).build())
+                .addChild(getNotificationEntryBuilder().setId(3).build())
                 .build();
         fireAddEvents(List.of(group));
         final NotificationEntry child0 = group.getChildren().get(0);
@@ -324,19 +426,21 @@
     }
 
     private static class FakeNotifInflater implements NotifInflater {
-        private Map<NotificationEntry, InflationCallback> mInflateCallbacks = new HashMap<>();
+        private final Map<NotificationEntry, InflationCallback> mInflateCallbacks = new HashMap<>();
 
         @Override
-        public void inflateViews(NotificationEntry entry, InflationCallback callback) {
+        public void inflateViews(@NonNull NotificationEntry entry, @NonNull Params params,
+                @NonNull InflationCallback callback) {
             mInflateCallbacks.put(entry, callback);
         }
 
         @Override
-        public void rebindViews(NotificationEntry entry, InflationCallback callback) {
+        public void rebindViews(@NonNull NotificationEntry entry, @NonNull Params params,
+                @NonNull InflationCallback callback) {
         }
 
         @Override
-        public void abortInflation(NotificationEntry entry) {
+        public void abortInflation(@NonNull NotificationEntry entry) {
         }
 
         public InflationCallback getInflateCallback(NotificationEntry entry) {
@@ -365,4 +469,12 @@
     private static final String TEST_GROUP_KEY = "TEST_GROUP_KEY";
     private static final int TEST_CHILD_BIND_CUTOFF = 9;
     private static final int TEST_MAX_GROUP_DELAY = 100;
+
+    private class TestableAdjustmentProvider extends NotifUiAdjustmentProvider {
+        private void setSectionIsLowPriority(boolean lowPriority) {
+            setLowPrioritySections(lowPriority
+                    ? Collections.singleton(mNotifSection.getSectioner())
+                    : Collections.emptyList());
+        }
+    }
 }
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 2091cf8..abe33aa 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
@@ -19,6 +19,7 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
@@ -28,8 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
+import android.app.NotificationManager;
 import android.testing.AndroidTestingRunner;
 
 import androidx.annotation.Nullable;
@@ -38,10 +38,12 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SbnBuilder;
 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.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
 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;
@@ -54,7 +56,6 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -66,12 +67,11 @@
 
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private HighPriorityProvider mHighPriorityProvider;
+    @Mock private NotifUiAdjustmentProvider mAdjustmentProvider;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private NodeController mAlertingHeaderController;
     @Mock private NodeController mSilentNodeController;
     @Mock private SectionHeaderController mSilentHeaderController;
-    @Mock private NotificationListenerService.Ranking mRanking;
-    @Mock private StatusBarNotification mSbn;
 
     @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
 
@@ -89,12 +89,14 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mRankingCoordinator = new RankingCoordinator(
-                mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
-                mSilentHeaderController, mSilentNodeController);
+                mStatusBarStateController,
+                mHighPriorityProvider,
+                mAdjustmentProvider,
+                mAlertingHeaderController,
+                mSilentHeaderController,
+                mSilentNodeController);
         mEntry = spy(new NotificationEntryBuilder().build());
-        mRanking = spy(getRankingForUnfilteredNotif().build());
-        mEntry.setRanking(mRanking);
-        when(mEntry.getSbn()).thenReturn(mSbn);
+        mEntry.setRanking(getRankingForUnfilteredNotif().build());
 
         mRankingCoordinator.attach(mNotifPipeline);
         verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
@@ -109,23 +111,19 @@
 
     @Test
     public void testSilentHeaderClearableChildrenUpdate() {
-        StatusBarNotification sbn = Mockito.mock(StatusBarNotification.class);
-        Mockito.doReturn("key").when(sbn).getKey();
-        Mockito.doReturn(Mockito.mock(Notification.class)).when(sbn).getNotification();
-        NotificationEntry entry = new NotificationEntryBuilder().setSbn(sbn).build();
-        ListEntry listEntry = new ListEntry("key", 0L) {
+        ListEntry listEntry = new ListEntry(mEntry.getKey(), 0L) {
             @Nullable
             @Override
             public NotificationEntry getRepresentativeEntry() {
-                return entry;
+                return mEntry;
             }
         };
-        Mockito.doReturn(true).when(sbn).isClearable();
+        setRankingAmbient(false);
+        setSbnClearable(true);
         mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
-        when(mRanking.isAmbient()).thenReturn(false);
         verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
-        mRankingCoordinator.resetClearAllFlags();
-        Mockito.doReturn(false).when(sbn).isClearable();
+
+        setSbnClearable(false);
         mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
         verify(mSilentHeaderController).setClearSectionEnabled(eq(false));
     }
@@ -204,7 +202,7 @@
     public void testIncludeInSectionSilent() {
         // GIVEN the entry isn't high priority
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
-        when(mRanking.isAmbient()).thenReturn(false);
+        setRankingAmbient(false);
 
         // THEN entry is in the silent section
         assertFalse(mAlertingSectioner.isInSection(mEntry));
@@ -213,24 +211,23 @@
 
     @Test
     public void testMinSection() {
-        when(mEntry.getRanking()).thenReturn(mRanking);
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
-        when(mRanking.isAmbient()).thenReturn(true);
+        setRankingAmbient(true);
         assertInSection(mEntry, mMinimizedSectioner);
     }
 
     @Test
     public void testSilentSection() {
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
-        when(mRanking.isAmbient()).thenReturn(false);
+        setRankingAmbient(false);
         assertInSection(mEntry, mSilentSectioner);
     }
 
     @Test
     public void testClearableSilentSection() {
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
-        when(mSbn.isClearable()).thenReturn(true);
-        when(mRanking.isAmbient()).thenReturn(false);
+        setSbnClearable(true);
+        setRankingAmbient(false);
         mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
         verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
     }
@@ -238,17 +235,17 @@
     @Test
     public void testClearableMinimizedSection() {
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
-        when(mSbn.isClearable()).thenReturn(true);
-        when(mRanking.isAmbient()).thenReturn(true);
+        setSbnClearable(true);
+        setRankingAmbient(true);
         mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
         verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
     }
 
     @Test
     public void testNotClearableSilentSection() {
-        when(mSbn.isClearable()).thenReturn(false);
+        setSbnClearable(false);
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
-        when(mRanking.isAmbient()).thenReturn(false);
+        setRankingAmbient(false);
         mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
         mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
         mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
@@ -257,9 +254,9 @@
 
     @Test
     public void testNotClearableMinimizedSection() {
-        when(mSbn.isClearable()).thenReturn(false);
+        setSbnClearable(false);
         when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
-        when(mRanking.isAmbient()).thenReturn(true);
+        setRankingAmbient(true);
         mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
         mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
         mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
@@ -277,9 +274,24 @@
     }
 
     private RankingBuilder getRankingForUnfilteredNotif() {
-        return new RankingBuilder()
-                .setKey(mEntry.getKey())
+        return new RankingBuilder(mEntry.getRanking())
                 .setSuppressedVisualEffects(0)
                 .setSuspended(false);
     }
+
+    private void setSbnClearable(boolean clearable) {
+        mEntry.setSbn(new SbnBuilder(mEntry.getSbn())
+                .setFlag(mContext, Notification.FLAG_NO_CLEAR, !clearable)
+                .build());
+        assertEquals(clearable, mEntry.getSbn().isClearable());
+    }
+
+    private void setRankingAmbient(boolean ambient) {
+        mEntry.setRanking(new RankingBuilder(mEntry.getRanking())
+                .setImportance(ambient
+                        ? NotificationManager.IMPORTANCE_MIN
+                        : NotificationManager.IMPORTANCE_DEFAULT)
+                .build());
+        assertEquals(ambient, mEntry.getRanking().isAmbient());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 4edca7d..5df1d28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -19,8 +19,10 @@
 import static junit.framework.Assert.assertFalse;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -30,6 +32,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -37,7 +40,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -57,9 +59,7 @@
 
     private VisualStabilityCoordinator mCoordinator;
 
-    // captured listeners and pluggables:
-    private NotifCollectionListener mCollectionListener;
-
+    @Mock private DumpManager mDumpManager;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock private StatusBarStateController mStatusBarStateController;
@@ -69,7 +69,6 @@
     @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
     @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
     @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
-    @Captor private ArgumentCaptor<NotifCollectionListener> mNotifCollectionListenerCaptor;
 
     private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -84,6 +83,7 @@
         MockitoAnnotations.initMocks(this);
 
         mCoordinator = new VisualStabilityCoordinator(
+                mDumpManager,
                 mHeadsUpManager,
                 mWakefulnessLifecycle,
                 mStatusBarStateController,
@@ -107,6 +107,12 @@
                 .build();
 
         when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(false);
+
+        // Whenever we invalidate, the pipeline runs again, so we invalidate the state
+        doAnswer(i -> {
+            mNotifStabilityManager.onBeginRun();
+            return null;
+        }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager));
     }
 
     @Test
@@ -211,7 +217,7 @@
         mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis());
 
         // THEN the notification list is invalidated
-        verifyInvalidateCalled(true);
+        verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
     }
 
     @Test
@@ -225,7 +231,7 @@
         mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
 
         // THEN invalidate is not called because this entry was never suppressed from reordering
-        verifyInvalidateCalled(false);
+        verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
     }
 
     @Test
@@ -241,7 +247,7 @@
 
         // THEN invalidate is not called because this entry was never suppressed from reordering;
         // THEN section changes are allowed for this notification
-        verifyInvalidateCalled(false);
+        verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
         assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
 
         // WHEN we're pulsing (now disallowing reordering)
@@ -268,13 +274,14 @@
 
         // WHEN we temporarily allow section changes for this notification entry
         mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-        verifyInvalidateCalled(true); // can now reorder, so invalidates
+        // can now reorder, so invalidates
+        verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
 
         // WHEN reordering is now allowed because device isn't pulsing anymore
         setPulsing(false);
 
-        // THEN invalidate isn't called since reordering was already allowed
-        verifyInvalidateCalled(false);
+        // THEN invalidate isn't called a second time since reordering was already allowed
+        verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
     }
 
     @Test
@@ -292,7 +299,7 @@
 
         // THEN we never see any calls to invalidate since there weren't any notifications that
         // were being suppressed from grouping or section changes
-        verifyInvalidateCalled(false);
+        verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
     }
 
     @Test
@@ -308,7 +315,41 @@
         setPanelExpanded(false);
 
         //  invalidate is called because we were previously suppressing a group change
-        verifyInvalidateCalled(true);
+        verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+    }
+
+    @Test
+    public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
+        // GIVEN visual stability is being maintained b/c panel is expanded
+        setPulsing(false);
+        setScreenOn(true);
+        setPanelExpanded(true);
+
+        assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+        // The pipeline still has to report back that entry reordering was suppressed
+        mNotifStabilityManager.onEntryReorderSuppressed();
+
+        // WHEN the panel isn't expanded anymore
+        setPanelExpanded(false);
+
+        //  invalidate is called because we were previously suppressing an entry reorder
+        verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+    }
+
+    @Test
+    public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
+        // GIVEN visual stability is being maintained b/c panel is expanded
+        setPulsing(false);
+        setScreenOn(true);
+        setPanelExpanded(true);
+
+        assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+
+        // WHEN the panel isn't expanded anymore
+        setPanelExpanded(false);
+
+        // invalidate is not called because we were not told that an entry reorder was suppressed
+        verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
     }
 
     @Test
@@ -345,13 +386,4 @@
         mStatusBarStateListener.onExpandedChanged(expanded);
     }
 
-    private void verifyInvalidateCalled(boolean invalidateCalled) {
-        if (invalidateCalled) {
-            verify(mInvalidateListener).onPluggableInvalidated(mNotifStabilityManager);
-        } else {
-            verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
-        }
-
-        reset(mInvalidateListener);
-    }
 }
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 ed42ac3..a53fb78 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
@@ -70,8 +70,8 @@
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 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.LowPriorityInflationHelper;
 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;
@@ -179,7 +179,6 @@
                 .setNotification(notification)
                 .build();
 
-        when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
 
         mEntryManager = new NotificationEntryManager(
@@ -283,6 +282,7 @@
 
         mRowBinder = new NotificationRowBinderImpl(
                 mContext,
+                mFeatureFlags,
                 new NotificationMessagingUtil(mContext),
                 mRemoteInputManager,
                 mLockscreenUserManager,
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 f11f8c4..58e3cc8 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
@@ -48,6 +48,7 @@
 
 import com.android.systemui.ActivityStarterDelegate;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -79,6 +80,7 @@
     @Rule public final MockitoRule mockitoRule = MockitoJUnit.rule();
 
     @Mock private NotificationStackScrollLayout mNssl;
+    @Mock private FeatureFlags mFeatureFlags;
     @Mock private ActivityStarterDelegate mActivityStarterDelegate;
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private ConfigurationController mConfigurationController;
@@ -120,6 +122,7 @@
         when(mSilentHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
         mSectionsManager =
                 new NotificationSectionsManager(
+                        mFeatureFlags,
                         mStatusBarStateController,
                         mConfigurationController,
                         mKeyguardMediaController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index f89bbe8..766471b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -152,7 +152,7 @@
     }
 
     @Test
-    public void onWallpaperColorsChanged_setsTheme() {
+    public void onWallpaperColorsChanged_setsTheme_whenForeground() {
         // Should ask for a new theme when wallpaper colors change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
@@ -180,13 +180,43 @@
 
         // But should change theme after changing wallpapers
         clearInvocations(mThemeOverlayApplier);
-        mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+        Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+        intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
+        mBroadcastReceiver.getValue().onReceive(null, intent);
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
                 null, null), WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
     }
 
     @Test
+    public void onWallpaperColorsChanged_setsTheme_skipWhenBackground() {
+        // Should ask for a new theme when wallpaper colors change
+        WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+                Color.valueOf(Color.BLUE), null);
+        mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+        ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
+                ArgumentCaptor.forClass(Map.class);
+
+        verify(mThemeOverlayApplier)
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+
+        // Assert that we received the colors that we were expecting
+        assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
+                .isEqualTo(new OverlayIdentifier("ffff0000"));
+        assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
+                .isEqualTo(new OverlayIdentifier("ffff0000"));
+
+        // Should not change theme after changing wallpapers, if intent doesn't have
+        // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
+        clearInvocations(mThemeOverlayApplier);
+        mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+        mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
+                null, null), WallpaperManager.FLAG_SYSTEM);
+        verify(mThemeOverlayApplier, never())
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+    }
+
+    @Test
     public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() {
         // Should ask for a new theme when wallpaper colors change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -455,7 +485,9 @@
         // Regression test: null events should not reset the internal state and allow colors to be
         // applied again.
         clearInvocations(mThemeOverlayApplier);
-        mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+        Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+        intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
+        mBroadcastReceiver.getValue().onReceive(null, intent);
         mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
                 any());
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 0e03434..25814ec 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -57,6 +57,8 @@
     Watchdog watchdog = 2;
     HalfWatchdog half_watchdog = 3;
     AppNotResponding anr = 4;
+    JavaCrash java_crash = 5;
+    NativeCrash native_crash = 6;
   }
 
   message Watchdog {
@@ -99,6 +101,47 @@
     optional ProcessClass process_class = 5;
   }
 
+  message JavaCrash {
+    // The crash exception class.
+    // Optional, may be redacted for privacy.
+    optional string exception_class = 1;
+
+    // Name of the crashed process.
+    // Optional, may be redacted for privacy.
+    optional string process = 2;
+
+    // PID of the crashed process.
+    // Required.
+    optional int32 pid = 3;
+
+    // UID of the crashed process.
+    // Required.
+    optional int32 uid = 4;
+
+    // Category of the crashed process (DATA_APP, SYSTEM_APP, etc).
+    // Required.
+    optional ProcessClass process_class = 5;
+  }
+
+  message NativeCrash {
+    // Name of the crashed process.
+    // Optional, may be redacted for privacy.
+    optional string process = 1;
+
+    // PID of the crashed process.
+    // Required.
+    optional int32 pid = 2;
+
+    // UID of the crashed process.
+    // Required.
+    optional int32 uid = 3;
+
+    // Category of the crashed process (DATA_APP, SYSTEM_APP, etc).
+    // Required.
+    optional ProcessClass process_class = 4;
+  }
+
+
   // Mirrors definition & values in {@link android.server.ServerProtoEnums}.
   enum ProcessClass {
     PROCESS_CLASS_UNKNOWN = 0;
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 c62473d..51e3417 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -17,6 +17,7 @@
 package com.android.server.accessibility.magnification;
 
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
@@ -66,9 +67,6 @@
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "FullScreenMagnificationController";
 
-    private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
-    };
-
     private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
 
     private final Object mLock;
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 bfaab9a..e1ec273 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -18,6 +18,7 @@
 
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
+import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -74,7 +75,7 @@
         public void onReceive(Context context, Intent intent) {
             final int displayId = context.getDisplayId();
             removeMagnificationButton(displayId);
-            disableWindowMagnification(displayId, false);
+            disableWindowMagnification(displayId, false, null);
         }
     };
 
@@ -272,7 +273,7 @@
      *                or {@link Float#NaN} to leave unchanged.
      */
     void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
-        enableWindowMagnification(displayId, scale, centerX, centerY, null);
+        enableWindowMagnification(displayId, scale, centerX, centerY, STUB_ANIMATION_CALLBACK);
     }
 
     /**
@@ -314,7 +315,7 @@
      * @param clear {@true} Clears the state of window magnification.
      */
     void disableWindowMagnification(int displayId, boolean clear) {
-        disableWindowMagnification(displayId, clear, null);
+        disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK);
     }
 
     /**
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 1fd7951..2645f3f 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -17,6 +17,7 @@
 package com.android.server.companion;
 
 import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
 import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
@@ -32,6 +33,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.role.RoleManager;
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
@@ -41,8 +43,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.Signature;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.PackageUtils;
 import android.util.Slog;
@@ -56,6 +60,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -69,6 +74,8 @@
         map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
         map.put(DEVICE_PROFILE_APP_STREAMING,
                 Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
+        map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
+                Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION);
 
         DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
     }
@@ -87,12 +94,14 @@
     private IFindDeviceCallback mFindDeviceCallback;
     private String mCallingPackage;
     private AndroidFuture<?> mOngoingDeviceDiscovery;
+    private RoleManager mRoleManager;
 
     private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
 
-    AssociationRequestsProcessor(CompanionDeviceManagerService service) {
+    AssociationRequestsProcessor(CompanionDeviceManagerService service, RoleManager roleManager) {
         mContext = service.getContext();
         mService = service;
+        mRoleManager = roleManager;
 
         final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
         mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -165,6 +174,16 @@
                 }));
     }
 
+    private boolean isRoleHolder(int userId, String packageName, String role) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            List<String> holders = mRoleManager.getRoleHoldersAsUser(role, UserHandle.of(userId));
+            return holders.contains(packageName);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     void stopScan(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) {
         if (DEBUG) {
             Slog.d(TAG, "stopScan(request = " + request + ")");
@@ -186,6 +205,12 @@
                     "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
         }
 
+        if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
+            // TODO: remove, when properly supporting this profile.
+            throw new UnsupportedOperationException(
+                    "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
+        }
+
         if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
             throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
         }
@@ -217,6 +242,12 @@
     }
 
     private boolean mayAssociateWithoutPrompt(String packageName, int userId) {
+        if (mRequest.getDeviceProfile() != null
+                && isRoleHolder(userId, packageName, mRequest.getDeviceProfile())) {
+            // Don't need to collect user's consent since app already holds the role.
+            return true;
+        }
+
         String[] sameOemPackages = mContext.getResources()
                 .getStringArray(com.android.internal.R.array.config_companionDevicePackages);
         if (!ArrayUtils.contains(sameOemPackages, packageName)) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index f648afa..d5357dc 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -200,7 +200,6 @@
         super(context);
         mImpl = new CompanionDeviceManagerImpl();
         mPersistentDataStore = new PersistentDataStore();
-        mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
 
         mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
         mRoleManager = context.getSystemService(RoleManager.class);
@@ -213,6 +212,7 @@
                 context.getSystemService(PermissionControllerManager.class));
         mUserManager = context.getSystemService(UserManager.class);
         mCompanionDevicePresenceController = new CompanionDevicePresenceController();
+        mAssociationRequestsProcessor = new AssociationRequestsProcessor(this, mRoleManager);
 
         registerPackageMonitor();
     }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 16343e5..9f22489 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -149,9 +149,11 @@
         "android.hardware.boot-V1.1-java",
         "android.hardware.boot-V1.2-java",
         "android.hardware.broadcastradio-V2.0-java",
-        "android.hardware.health-V1.0-java",
-        "android.hardware.health-V2.0-java",
-        "android.hardware.health-V2.1-java",
+        "android.hardware.health-V1.0-java", // HIDL
+        "android.hardware.health-V2.0-java", // HIDL
+        "android.hardware.health-V2.1-java", // HIDL
+        "android.hardware.health-V1-java", // AIDL
+        "android.hardware.health-translate-java",
         "android.hardware.light-V1-java",
         "android.hardware.tv.cec-V1.1-java",
         "android.hardware.weaver-V1.0-java",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 728efa5..844ac86 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -17,7 +17,7 @@
 package com.android.server;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.server.health.Utils.copy;
+import static com.android.server.health.Utils.copyV1Battery;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -26,7 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.ContentObserver;
-import android.hardware.health.V1_0.HealthInfo;
+import android.hardware.health.HealthInfo;
 import android.hardware.health.V2_1.BatteryCapacityLevel;
 import android.metrics.LogMaker;
 import android.os.BatteryManager;
@@ -138,7 +138,6 @@
 
     private HealthInfo mHealthInfo;
     private final HealthInfo mLastHealthInfo = new HealthInfo();
-    private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
     private boolean mBatteryLevelCritical;
     private int mLastBatteryStatus;
     private int mLastBatteryHealth;
@@ -354,8 +353,8 @@
     }
 
     private boolean shouldShutdownLocked() {
-        if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
-            return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
+        if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
+            return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
         }
         if (mHealthInfo.batteryLevel > 0) {
             return false;
@@ -397,7 +396,7 @@
         // shut down gracefully if temperature is too high (> 68.0C by default)
         // wait until the system has booted before attempting to display the
         // shutdown dialog.
-        if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) {
+        if (mHealthInfo.batteryTemperatureTenthsCelsius > mShutdownBatteryTemperature) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -414,27 +413,23 @@
         }
     }
 
-    private void update(android.hardware.health.V2_1.HealthInfo info) {
+    private void update(android.hardware.health.HealthInfo info) {
         traceBegin("HealthInfoUpdate");
 
-        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
-                info.legacy.legacy.batteryChargeCounter);
-        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
-                info.legacy.legacy.batteryCurrent);
-        Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType",
-                plugType(info.legacy.legacy));
-        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus",
-                info.legacy.legacy.batteryStatus);
+        Trace.traceCounter(
+                Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah);
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps);
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info));
+        Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus);
 
         synchronized (mLock) {
             if (!mUpdatesStopped) {
-                mHealthInfo = info.legacy.legacy;
-                mHealthInfo2p1 = info;
+                mHealthInfo = info;
                 // Process the new values.
                 processValuesLocked(false);
                 mLock.notifyAll(); // for any waiters on new info
             } else {
-                copy(mLastHealthInfo, info.legacy.legacy);
+                copyV1Battery(mLastHealthInfo, info);
             }
         }
         traceEnd();
@@ -470,11 +465,16 @@
 
         // Let the battery stats keep track of the current level.
         try {
-            mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
-                    mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
-                    mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
-                    mHealthInfo.batteryFullCharge,
-                    mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
+            mBatteryStats.setBatteryState(
+                    mHealthInfo.batteryStatus,
+                    mHealthInfo.batteryHealth,
+                    mPlugType,
+                    mHealthInfo.batteryLevel,
+                    mHealthInfo.batteryTemperatureTenthsCelsius,
+                    mHealthInfo.batteryVoltageMillivolts,
+                    mHealthInfo.batteryChargeCounterUah,
+                    mHealthInfo.batteryFullChargeUah,
+                    mHealthInfo.batteryChargeTimeToFullNowSeconds);
         } catch (RemoteException e) {
             // Should never happen.
         }
@@ -482,17 +482,18 @@
         shutdownIfNoPowerLocked();
         shutdownIfOverTempLocked();
 
-        if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
-                mHealthInfo.batteryHealth != mLastBatteryHealth ||
-                mHealthInfo.batteryPresent != mLastBatteryPresent ||
-                mHealthInfo.batteryLevel != mLastBatteryLevel ||
-                mPlugType != mLastPlugType ||
-                mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
-                mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
-                mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
-                mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
-                mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
-                mInvalidCharger != mLastInvalidCharger)) {
+        if (force
+                || (mHealthInfo.batteryStatus != mLastBatteryStatus
+                        || mHealthInfo.batteryHealth != mLastBatteryHealth
+                        || mHealthInfo.batteryPresent != mLastBatteryPresent
+                        || mHealthInfo.batteryLevel != mLastBatteryLevel
+                        || mPlugType != mLastPlugType
+                        || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
+                        || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
+                        || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
+                        || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
+                        || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
+                        || mInvalidCharger != mLastInvalidCharger)) {
 
             if (mPlugType != mLastPlugType) {
                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -549,8 +550,11 @@
             if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
                 // Don't do this just from voltage or temperature changes, that is
                 // too noisy.
-                EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
-                        mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
+                EventLog.writeEvent(
+                        EventLogTags.BATTERY_LEVEL,
+                        mHealthInfo.batteryLevel,
+                        mHealthInfo.batteryVoltageMillivolts,
+                        mHealthInfo.batteryTemperatureTenthsCelsius);
             }
             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
                     mPlugType == BATTERY_PLUGGED_NONE) {
@@ -656,11 +660,11 @@
             mLastBatteryPresent = mHealthInfo.batteryPresent;
             mLastBatteryLevel = mHealthInfo.batteryLevel;
             mLastPlugType = mPlugType;
-            mLastBatteryVoltage = mHealthInfo.batteryVoltage;
-            mLastBatteryTemperature = mHealthInfo.batteryTemperature;
-            mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
-            mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
-            mLastChargeCounter = mHealthInfo.batteryChargeCounter;
+            mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
+            mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
+            mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
+            mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
+            mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
             mLastBatteryLevelCritical = mBatteryLevelCritical;
             mLastInvalidCharger = mInvalidCharger;
         }
@@ -683,13 +687,17 @@
         intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
         intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
         intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
-        intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
-        intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
+        intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+        intent.putExtra(
+                BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
-        intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
-        intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
-        intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+        intent.putExtra(
+                BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrentMicroamps);
+        intent.putExtra(
+                BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE,
+                mHealthInfo.maxChargingVoltageMicrovolts);
+        intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
         if (DEBUG) {
             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
                     + ", info:" + mHealthInfo.toString());
@@ -709,9 +717,9 @@
         event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
         event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
         event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
-        event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
-        event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
-        event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+        event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+        event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
+        event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
         event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
 
         boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
@@ -903,7 +911,7 @@
                 }
                 try {
                     if (!mUpdatesStopped) {
-                        copy(mLastHealthInfo, mHealthInfo);
+                        copyV1Battery(mLastHealthInfo, mHealthInfo);
                     }
                     boolean update = true;
                     switch (key) {
@@ -926,10 +934,10 @@
                             mHealthInfo.batteryLevel = Integer.parseInt(value);
                             break;
                         case "counter":
-                            mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
+                            mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value);
                             break;
                         case "temp":
-                            mHealthInfo.batteryTemperature = Integer.parseInt(value);
+                            mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value);
                             break;
                         case "invalid":
                             mInvalidCharger = Integer.parseInt(value);
@@ -973,7 +981,7 @@
 
     private void setChargerAcOnline(boolean online, boolean forceUpdate) {
         if (!mUpdatesStopped) {
-            copy(mLastHealthInfo, mHealthInfo);
+            copyV1Battery(mLastHealthInfo, mHealthInfo);
         }
         mHealthInfo.chargerAcOnline = online;
         mUpdatesStopped = true;
@@ -982,7 +990,7 @@
 
     private void setBatteryLevel(int level, boolean forceUpdate) {
         if (!mUpdatesStopped) {
-            copy(mLastHealthInfo, mHealthInfo);
+            copyV1Battery(mLastHealthInfo, mHealthInfo);
         }
         mHealthInfo.batteryLevel = level;
         mUpdatesStopped = true;
@@ -991,7 +999,7 @@
 
     private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
         if (!mUpdatesStopped) {
-            copy(mLastHealthInfo, mHealthInfo);
+            copyV1Battery(mLastHealthInfo, mHealthInfo);
         }
         mHealthInfo.chargerAcOnline = false;
         mHealthInfo.chargerUsbOnline = false;
@@ -1003,7 +1011,7 @@
     private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
         if (mUpdatesStopped) {
             mUpdatesStopped = false;
-            copy(mHealthInfo, mLastHealthInfo);
+            copyV1Battery(mHealthInfo, mLastHealthInfo);
             Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
         }
         if (mBatteryInputSuspended) {
@@ -1038,16 +1046,16 @@
                 pw.println("  AC powered: " + mHealthInfo.chargerAcOnline);
                 pw.println("  USB powered: " + mHealthInfo.chargerUsbOnline);
                 pw.println("  Wireless powered: " + mHealthInfo.chargerWirelessOnline);
-                pw.println("  Max charging current: " + mHealthInfo.maxChargingCurrent);
-                pw.println("  Max charging voltage: " + mHealthInfo.maxChargingVoltage);
-                pw.println("  Charge counter: " + mHealthInfo.batteryChargeCounter);
+                pw.println("  Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps);
+                pw.println("  Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts);
+                pw.println("  Charge counter: " + mHealthInfo.batteryChargeCounterUah);
                 pw.println("  status: " + mHealthInfo.batteryStatus);
                 pw.println("  health: " + mHealthInfo.batteryHealth);
                 pw.println("  present: " + mHealthInfo.batteryPresent);
                 pw.println("  level: " + mHealthInfo.batteryLevel);
                 pw.println("  scale: " + BATTERY_SCALE);
-                pw.println("  voltage: " + mHealthInfo.batteryVoltage);
-                pw.println("  temperature: " + mHealthInfo.batteryTemperature);
+                pw.println("  voltage: " + mHealthInfo.batteryVoltageMillivolts);
+                pw.println("  temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius);
                 pw.println("  technology: " + mHealthInfo.batteryTechnology);
             } else {
                 Shell shell = new Shell();
@@ -1070,16 +1078,23 @@
                 batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS;
             }
             proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
-            proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
-            proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
-            proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+            proto.write(
+                    BatteryServiceDumpProto.MAX_CHARGING_CURRENT,
+                    mHealthInfo.maxChargingCurrentMicroamps);
+            proto.write(
+                    BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE,
+                    mHealthInfo.maxChargingVoltageMicrovolts);
+            proto.write(
+                    BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
             proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
             proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
             proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
             proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
             proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
-            proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage);
-            proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature);
+            proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+            proto.write(
+                    BatteryServiceDumpProto.TEMPERATURE,
+                    mHealthInfo.batteryTemperatureTenthsCelsius);
             proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
         }
         proto.flush();
@@ -1207,14 +1222,14 @@
         @Override
         public int getBatteryChargeCounter() {
             synchronized (mLock) {
-                return mHealthInfo.batteryChargeCounter;
+                return mHealthInfo.batteryChargeCounterUah;
             }
         }
 
         @Override
         public int getBatteryFullCharge() {
             synchronized (mLock) {
-                return mHealthInfo.batteryFullCharge;
+                return mHealthInfo.batteryFullChargeUah;
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0eed190..4c2cd53 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8049,11 +8049,16 @@
                 crashInfo.throwFileName,
                 crashInfo.throwLineNumber);
 
+        int processClassEnum = processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
+                : (r != null) ? r.getProcessClassEnum()
+                        : ServerProtoEnums.ERROR_SOURCE_UNKNOWN;
+        int uid = (r != null) ? r.uid : -1;
+        int pid = (r != null) ? r.getPid() : -1;
         FrameworkStatsLog.write(FrameworkStatsLog.APP_CRASH_OCCURRED,
-                (r != null) ? r.uid : -1,
+                uid,
                 eventType,
                 processName,
-                (r != null) ? r.getPid() : -1,
+                pid,
                 (r != null && r.info != null) ? r.info.packageName : "",
                 (r != null && r.info != null) ? (r.info.isInstantApp()
                         ? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
@@ -8063,9 +8068,7 @@
                         ? FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__FOREGROUND
                         : FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__BACKGROUND)
                         : FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN,
-                processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
-                        : (r != null) ? r.getProcessClassEnum()
-                                      : ServerProtoEnums.ERROR_SOURCE_UNKNOWN,
+                processClassEnum,
                 incrementalMetrics != null /* isIncremental */, loadingProgress,
                 incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
                         : -1,
@@ -8092,6 +8095,13 @@
                         : -1
         );
 
+        if (eventType.equals("native_crash")) {
+            CriticalEventLog.getInstance().logNativeCrash(processClassEnum, processName, uid, pid);
+        } else if (eventType.equals("crash")) {
+            CriticalEventLog.getInstance().logJavaCrash(crashInfo.exceptionClassName,
+                    processClassEnum, processName, uid, pid);
+        }
+
         final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
                         : r.getWindowProcessController().computeRelaunchReason();
         final String relaunchReasonString = relaunchReasonToString(relaunchReason);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b144c8d..5b33a71 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -87,6 +87,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
@@ -1889,16 +1890,21 @@
 
         int userId = Integer.parseInt(getNextArgRequired());
         boolean switched;
-        if (wait) {
-            switched = switchUserAndWaitForComplete(userId);
-        } else {
-            switched = mInterface.switchUser(userId);
-        }
-        if (switched) {
-            return 0;
-        } else {
-            pw.printf("Error: Failed to switch to user %d\n", userId);
-            return 1;
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shell_runSwitchUser");
+        try {
+            if (wait) {
+                switched = switchUserAndWaitForComplete(userId);
+            } else {
+                switched = mInterface.switchUser(userId);
+            }
+            if (switched) {
+                return 0;
+            } else {
+                pw.printf("Error: Failed to switch to user %d\n", userId);
+                return 1;
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index b5a9ee9..c48ff9f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -97,6 +97,7 @@
         DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
         DeviceConfig.NAMESPACE_TETHERING,
+        DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE,
         DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
     };
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6dbdead..16d7f1d 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1406,7 +1406,7 @@
             @Nullable IProgressListener unlockListener) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
 
-        t.traceBegin("startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
+        t.traceBegin("UserController.startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
         try {
             return startUserInternal(userId, foreground, unlockListener, t);
         } finally {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e0775d4..f42870b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1381,7 +1381,8 @@
 
                 Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first
                         + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
-                        + " requestId: " + requestId);
+                        + " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
+                        + promptInfo.isIgnoreEnrollmentState());
 
                 if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
                     // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index cd0ff10..a5a3542 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -83,6 +83,7 @@
     final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
     final boolean credentialAvailable;
     final boolean confirmationRequested;
+    final boolean ignoreEnrollmentState;
 
     static PreAuthInfo create(ITrustManager trustManager,
             DevicePolicyManager devicePolicyManager,
@@ -114,7 +115,8 @@
                 @AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
                         devicePolicyManager, settingObserver, sensor, userId, opPackageName,
                         checkDevicePolicyManager, requestedStrength,
-                        promptInfo.getAllowedSensorIds());
+                        promptInfo.getAllowedSensorIds(),
+                        promptInfo.isIgnoreEnrollmentState());
 
                 Slog.d(TAG, "Package: " + opPackageName
                         + " Sensor ID: " + sensor.id
@@ -130,7 +132,8 @@
         }
 
         return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
-                eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested);
+                eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
+                promptInfo.isIgnoreEnrollmentState());
     }
 
     /**
@@ -145,7 +148,8 @@
             BiometricService.SettingObserver settingObserver,
             BiometricSensor sensor, int userId, String opPackageName,
             boolean checkDevicePolicyManager, int requestedStrength,
-            @NonNull List<Integer> requestedSensorIds) {
+            @NonNull List<Integer> requestedSensorIds,
+            boolean ignoreEnrollmentState) {
 
         if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
             return BIOMETRIC_NO_HARDWARE;
@@ -167,7 +171,8 @@
                 return BIOMETRIC_HARDWARE_NOT_DETECTED;
             }
 
-            if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)) {
+            if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)
+                    && !ignoreEnrollmentState) {
                 return BIOMETRIC_NOT_ENROLLED;
             }
 
@@ -238,7 +243,7 @@
     private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
             boolean credentialRequested, List<BiometricSensor> eligibleSensors,
             List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
-            boolean confirmationRequested) {
+            boolean confirmationRequested, boolean ignoreEnrollmentState) {
         mBiometricRequested = biometricRequested;
         mBiometricStrengthRequested = biometricStrengthRequested;
         this.credentialRequested = credentialRequested;
@@ -247,6 +252,7 @@
         this.ineligibleSensors = ineligibleSensors;
         this.credentialAvailable = credentialAvailable;
         this.confirmationRequested = confirmationRequested;
+        this.ignoreEnrollmentState = ignoreEnrollmentState;
     }
 
     private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index af8e8c2..3e70ee5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -281,7 +281,7 @@
         @Override // Binder call
         public long authenticate(final IBinder token, final long operationId,
                 final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
-                final String opPackageName) {
+                final String opPackageName, boolean ignoreEnrollmentState) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int callingUserId = UserHandle.getCallingUserId();
@@ -333,7 +333,8 @@
                     && sensorProps != null && sensorProps.isAnyUdfpsType()) {
                 final long identity2 = Binder.clearCallingIdentity();
                 try {
-                    return authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+                    return authenticateWithPrompt(operationId, sensorProps, userId, receiver,
+                            ignoreEnrollmentState);
                 } finally {
                     Binder.restoreCallingIdentity(identity2);
                 }
@@ -347,7 +348,8 @@
                 final long operationId,
                 @NonNull final FingerprintSensorPropertiesInternal props,
                 final int userId,
-                final IFingerprintServiceReceiver receiver) {
+                final IFingerprintServiceReceiver receiver,
+                boolean ignoreEnrollmentState) {
 
             final Context context = getUiContext();
             final Executor executor = context.getMainExecutor();
@@ -368,6 +370,7 @@
                             })
                     .setAllowedSensorIds(new ArrayList<>(
                             Collections.singletonList(props.sensorId)))
+                    .setIgnoreEnrollmentState(ignoreEnrollmentState)
                     .build();
 
             final BiometricPrompt.AuthenticationCallback promptCallback =
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 2fda4a5..b506230 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -236,7 +236,18 @@
 
     private boolean shouldIntercept(ActivityInfo activityInfo) {
         if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return false;
-        return !isAppAllowed(activityInfo.applicationInfo);
+        ApplicationInfo appInfo = activityInfo.applicationInfo;
+        // Dreams are allowed to show, and don't require the showWhenLocked attribute.
+        if (isActiveDream(appInfo)) return false;
+
+        // If the activity doesn't have showWhenLocked enabled, disallow the activity.
+        final boolean showWhenLocked =
+                (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
+        if (!showWhenLocked) {
+            return true;
+        }
+
+        return !isAppAllowed(appInfo);
     }
 
     private final class BinderService extends ICommunalManager.Stub {
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 30b3524..ab480e8 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -29,6 +29,8 @@
 import com.android.server.criticalevents.nano.CriticalEventProto;
 import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
 import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
 import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
 
 import java.io.File;
@@ -179,8 +181,56 @@
         log(event);
     }
 
+    /**
+     * Logs a java crash.
+     *
+     * @param exceptionClass   the crash exception class.
+     * @param processClassEnum {@link android.server.ServerProtoEnums} value for the crashed
+     *                         process.
+     * @param processName      name of the crashed process.
+     * @param uid              uid of the crashed process.
+     * @param pid              pid of the crashed process.
+     */
+    public void logJavaCrash(String exceptionClass, int processClassEnum, String processName,
+            int uid, int pid) {
+        JavaCrash crash = new JavaCrash();
+        crash.exceptionClass = exceptionClass;
+        crash.processClass = processClassEnum;
+        crash.process = processName;
+        crash.uid = uid;
+        crash.pid = pid;
+        CriticalEventProto event = new CriticalEventProto();
+        event.setJavaCrash(crash);
+        log(event);
+    }
+
+    /**
+     * Logs a native crash.
+     *
+     * @param processClassEnum {@link android.server.ServerProtoEnums} value for the crashed
+     *                         process.
+     * @param processName      name of the crashed process.
+     * @param uid              uid of the crashed process.
+     * @param pid              pid of the crashed process.
+     */
+    public void logNativeCrash(int processClassEnum, String processName, int uid, int pid) {
+        NativeCrash crash = new NativeCrash();
+        crash.processClass = processClassEnum;
+        crash.process = processName;
+        crash.uid = uid;
+        crash.pid = pid;
+        CriticalEventProto event = new CriticalEventProto();
+        event.setNativeCrash(crash);
+        log(event);
+    }
+
     private void log(CriticalEventProto event) {
         event.timestampMs = getWallTimeMillis();
+        appendAndSave(event);
+    }
+
+    @VisibleForTesting
+    void appendAndSave(CriticalEventProto event) {
         mEvents.append(event);
         saveLogToFile();
     }
@@ -420,7 +470,18 @@
                 if (shouldSanitize(anr.processClass, anr.process, anr.uid)) {
                     return sanitizeAnr(event);
                 }
+            } else if (event.hasJavaCrash()) {
+                JavaCrash crash = event.getJavaCrash();
+                if (shouldSanitize(crash.processClass, crash.process, crash.uid)) {
+                    return sanitizeJavaCrash(event);
+                }
+            } else if (event.hasNativeCrash()) {
+                NativeCrash crash = event.getNativeCrash();
+                if (shouldSanitize(crash.processClass, crash.process, crash.uid)) {
+                    return sanitizeNativeCrash(event);
+                }
             }
+            // No redaction needed.
             return event;
         }
 
@@ -428,21 +489,52 @@
             boolean sameApp = processName != null && processName.equals(mTraceProcessName)
                     && mTraceUid == uid;
 
-            // Only sanitize when both the ANR event and trace file are for different data apps.
+            // Only sanitize when both the critical event and trace file are for different data
+            // apps.
             return processClassEnum == CriticalEventProto.DATA_APP
                     && mTraceProcessClassEnum == CriticalEventProto.DATA_APP
                     && !sameApp;
         }
 
         private static CriticalEventProto sanitizeAnr(CriticalEventProto base) {
-            CriticalEventProto sanitized = new CriticalEventProto();
-            sanitized.timestampMs = base.timestampMs;
             AppNotResponding anr = new AppNotResponding();
-            sanitized.setAnr(anr);
             // Do not set subject and process.
             anr.processClass = base.getAnr().processClass;
             anr.uid = base.getAnr().uid;
             anr.pid = base.getAnr().pid;
+
+            CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+            sanitized.setAnr(anr);
+            return sanitized;
+        }
+
+        private static CriticalEventProto sanitizeJavaCrash(CriticalEventProto base) {
+            JavaCrash crash = new JavaCrash();
+            // Do not set exceptionClass and process.
+            crash.processClass = base.getJavaCrash().processClass;
+            crash.uid = base.getJavaCrash().uid;
+            crash.pid = base.getJavaCrash().pid;
+
+            CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+            sanitized.setJavaCrash(crash);
+            return sanitized;
+        }
+
+        private static CriticalEventProto sanitizeNativeCrash(CriticalEventProto base) {
+            NativeCrash crash = new NativeCrash();
+            // Do not set process.
+            crash.processClass = base.getNativeCrash().processClass;
+            crash.uid = base.getNativeCrash().uid;
+            crash.pid = base.getNativeCrash().pid;
+
+            CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+            sanitized.setNativeCrash(crash);
+            return sanitized;
+        }
+
+        private static CriticalEventProto sanitizeCriticalEventProto(CriticalEventProto base) {
+            CriticalEventProto sanitized = new CriticalEventProto();
+            sanitized.timestampMs = base.timestampMs;
             return sanitized;
         }
     }
diff --git a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
index 6b4d7b7..7a66980 100644
--- a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
+++ b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.health;
 
+import static android.hardware.health.Translate.h2aTranslate;
+
 import android.annotation.NonNull;
 import android.hardware.health.V2_0.IHealth;
 import android.hardware.health.V2_0.Result;
@@ -64,12 +66,12 @@
         propsLatest.batteryChargeTimeToFullNowSeconds =
                 Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
 
-        mCallback.update(propsLatest);
+        mCallback.update(h2aTranslate(propsLatest));
     }
 
     @Override
     public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
-        mCallback.update(props);
+        mCallback.update(h2aTranslate(props));
     }
 
     // on new service registered
diff --git a/services/core/java/com/android/server/health/HealthInfoCallback.java b/services/core/java/com/android/server/health/HealthInfoCallback.java
index 8136ca0..c2a77fc 100644
--- a/services/core/java/com/android/server/health/HealthInfoCallback.java
+++ b/services/core/java/com/android/server/health/HealthInfoCallback.java
@@ -27,6 +27,5 @@
      *
      * @param props the new health info.
      */
-    // TODO(b/177269435): AIDL
-    void update(android.hardware.health.V2_1.HealthInfo props);
+    void update(android.hardware.health.HealthInfo props);
 }
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java
index 0b43f26..9b97554 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapper.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java
@@ -68,8 +68,7 @@
      *     service-specific error when calling {@code getHealthInfo}, e.g. it is unsupported.
      * @throws RemoteException for any transaction-level errors
      */
-    // TODO(b/177269435): AIDL
-    public abstract android.hardware.health.V1_0.HealthInfo getHealthInfo() throws RemoteException;
+    public abstract android.hardware.health.HealthInfo getHealthInfo() throws RemoteException;
 
     /**
      * Create a new HealthServiceWrapper instance.
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
index 3bff2f8..0301174 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
@@ -16,9 +16,11 @@
 
 package com.android.server.health;
 
+import static android.hardware.health.Translate.h2aTranslate;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.hardware.health.V1_0.HealthInfo;
+import android.hardware.health.HealthInfo;
 import android.hardware.health.V2_0.IHealth;
 import android.hardware.health.V2_0.Result;
 import android.hidl.manager.V1_0.IServiceManager;
@@ -155,7 +157,7 @@
         service.getHealthInfo(
                 (result, value) -> {
                     if (result == Result.SUCCESS) {
-                        ret.value = value.legacy;
+                        ret.value = h2aTranslate(value.legacy);
                     }
                 });
         return ret.value;
diff --git a/services/core/java/com/android/server/health/Utils.java b/services/core/java/com/android/server/health/Utils.java
index fc039eb..a8c978c 100644
--- a/services/core/java/com/android/server/health/Utils.java
+++ b/services/core/java/com/android/server/health/Utils.java
@@ -50,4 +50,36 @@
         dst.batteryChargeCounter = src.batteryChargeCounter;
         dst.batteryTechnology = src.batteryTechnology;
     }
+
+    /**
+     * Copy battery fields of {@link android.hardware.health.HealthInfo} V1. This excludes
+     * non-battery fields like {@link android.hardware.health.HealthInfo#diskStats diskStats} and
+     * {@link android.hardware.health.HealthInfo#storageInfos storageInfos}
+     *
+     * @param dst destination
+     * @param src source
+     */
+    public static void copyV1Battery(
+            android.hardware.health.HealthInfo dst, android.hardware.health.HealthInfo src) {
+        dst.chargerAcOnline = src.chargerAcOnline;
+        dst.chargerUsbOnline = src.chargerUsbOnline;
+        dst.chargerWirelessOnline = src.chargerWirelessOnline;
+        dst.maxChargingCurrentMicroamps = src.maxChargingCurrentMicroamps;
+        dst.maxChargingVoltageMicrovolts = src.maxChargingVoltageMicrovolts;
+        dst.batteryStatus = src.batteryStatus;
+        dst.batteryHealth = src.batteryHealth;
+        dst.batteryPresent = src.batteryPresent;
+        dst.batteryLevel = src.batteryLevel;
+        dst.batteryVoltageMillivolts = src.batteryVoltageMillivolts;
+        dst.batteryTemperatureTenthsCelsius = src.batteryTemperatureTenthsCelsius;
+        dst.batteryCurrentMicroamps = src.batteryCurrentMicroamps;
+        dst.batteryCycleCount = src.batteryCycleCount;
+        dst.batteryFullChargeUah = src.batteryFullChargeUah;
+        dst.batteryChargeCounterUah = src.batteryChargeCounterUah;
+        dst.batteryTechnology = src.batteryTechnology;
+        dst.batteryCurrentAverageMicroamps = src.batteryCurrentAverageMicroamps;
+        dst.batteryCapacityLevel = src.batteryCapacityLevel;
+        dst.batteryChargeTimeToFullNowSeconds = src.batteryChargeTimeToFullNowSeconds;
+        dst.batteryFullChargeDesignCapacityUah = src.batteryFullChargeDesignCapacityUah;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d14ef16..d74b3d7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1827,7 +1827,7 @@
                 broadcastFilterForAllUsers.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
                 mContext.registerReceiverAsUser(new ImmsBroadcastReceiverForAllUsers(),
                         UserHandle.ALL, broadcastFilterForAllUsers, null, null,
-                        Context.RECEIVER_NOT_EXPORTED);
+                        Context.RECEIVER_EXPORTED);
 
                 final String defaultImiId = mSettings.getSelectedInputMethod();
                 final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
@@ -2916,7 +2916,7 @@
             final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
             if (mStatusBar != null) {
                 mStatusBar.setImeWindowStatus(mCurTokenDisplayId, mCurToken, vis, backDisposition,
-                        needsToShowImeSwitcher, false /*isMultiClientImeEnabled*/);
+                        needsToShowImeSwitcher);
             }
             final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
             if (imi != null && needsToShowImeSwitcher) {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 31a82a3..42fed36 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -2151,6 +2151,23 @@
                 // Enabled notification listener only works within the same user.
                 return false;
             }
+            // Verify whether package name and controller UID.
+            // It will indirectly check whether the caller has obtained the package name and UID
+            // via ControllerInfo or with the valid package name visibility.
+            try {
+                int actualControllerUid = mContext.getPackageManager().getPackageUidAsUser(
+                        controllerPackageName,
+                        UserHandle.getUserId(controllerUid));
+                if (controllerUid != actualControllerUid) {
+                    Log.w(TAG, "Failed to check enabled notification listener. Package name and"
+                            + " UID doesn't match");
+                    return false;
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "Failed to check enabled notification listener. Package name doesn't"
+                        + " exist");
+                return false;
+            }
 
             if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName,
                     UserHandle.getUserHandleForUid(controllerUid))) {
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 28ae6a4..a15fc3e 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,11 +1,7 @@
 set noparent
 
-codewiz@google.com
-jchalard@google.com
+include platform/packages/modules/Connectivity:/OWNERS
+
 jsharkey@android.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
 sudheersai@google.com
 yamasani@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5516109..cc965d3 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2612,7 +2612,8 @@
     private int pullNotificationStates(int atomTag, List<StatsEvent> data) {
         switch(atomTag) {
             case PACKAGE_NOTIFICATION_PREFERENCES:
-                mPreferencesHelper.pullPackagePreferencesStats(data);
+                mPreferencesHelper.pullPackagePreferencesStats(data,
+                        getAllUsersNotificationPermissions());
                 break;
             case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
                 mPreferencesHelper.pullPackageChannelPreferencesStats(data);
@@ -5065,16 +5066,18 @@
             final DumpFilter filter = DumpFilter.parseFromArguments(args);
             final long token = Binder.clearCallingIdentity();
             try {
+                final ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions =
+                        getAllUsersNotificationPermissions();
                 if (filter.stats) {
-                    dumpJson(pw, filter);
+                    dumpJson(pw, filter, pkgPermissions);
                 } else if (filter.rvStats) {
                     dumpRemoteViewStats(pw, filter);
                 } else if (filter.proto) {
-                    dumpProto(fd, filter);
+                    dumpProto(fd, filter, pkgPermissions);
                 } else if (filter.criticalPriority) {
                     dumpNotificationRecords(pw, filter);
                 } else {
-                    dumpImpl(pw, filter);
+                    dumpImpl(pw, filter, pkgPermissions);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -5896,12 +5899,38 @@
         return null;
     }
 
-    private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter) {
+    // Gets packages that have requested notification permission, and whether that has been
+    // allowed/denied, for all users on the device.
+    // Returns a single map containing that info keyed by (uid, package name) for all users.
+    // Because this calls into mPermissionHelper, this method must never be called with a lock held.
+    @VisibleForTesting
+    protected ArrayMap<Pair<Integer, String>, Boolean> getAllUsersNotificationPermissions() {
+        // don't bother if migration is not enabled
+        if (!mEnableAppSettingMigration) {
+            return null;
+        }
+        ArrayMap<Pair<Integer, String>, Boolean> allPermissions = new ArrayMap<>();
+        final List<UserInfo> allUsers = mUm.getUsers();
+        // for each of these, get the package notification permissions that are associated
+        // with this user and add it to the map
+        for (UserInfo ui : allUsers) {
+            ArrayMap<Pair<Integer, String>, Boolean> userPermissions =
+                    mPermissionHelper.getNotificationPermissionValues(
+                            ui.getUserHandle().getIdentifier());
+            for (Pair<Integer, String> pair : userPermissions.keySet()) {
+                allPermissions.put(pair, userPermissions.get(pair));
+            }
+        }
+        return allPermissions;
+    }
+
+    private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
         JSONObject dump = new JSONObject();
         try {
             dump.put("service", "Notification Manager");
-            dump.put("bans", mPreferencesHelper.dumpBansJson(filter));
-            dump.put("ranking", mPreferencesHelper.dumpJson(filter));
+            dump.put("bans", mPreferencesHelper.dumpBansJson(filter, pkgPermissions));
+            dump.put("ranking", mPreferencesHelper.dumpJson(filter, pkgPermissions));
             dump.put("stats", mUsageStats.dumpJson(filter));
             dump.put("channels", mPreferencesHelper.dumpChannelsJson(filter));
         } catch (JSONException e) {
@@ -5919,7 +5948,8 @@
         stats.dump(REPORT_REMOTE_VIEWS, pw, filter);
     }
 
-    private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) {
+    private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
         synchronized (mNotificationLock) {
             int N = mNotificationList.size();
@@ -5986,7 +6016,7 @@
 
             long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG);
             mRankingHelper.dump(proto, filter);
-            mPreferencesHelper.dump(proto, filter);
+            mPreferencesHelper.dump(proto, filter, pkgPermissions);
             proto.end(rankingToken);
         }
 
@@ -6009,7 +6039,8 @@
         }
     }
 
-    void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
+    void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
         pw.print("Current Notification Manager state");
         if (filter.filtered) {
             pw.print(" (filtered to "); pw.print(filter); pw.print(")");
@@ -6088,7 +6119,7 @@
                 mRankingHelper.dump(pw, "    ", filter);
 
                 pw.println("\n Notification Preferences:");
-                mPreferencesHelper.dump(pw, "    ", filter);
+                mPreferencesHelper.dump(pw, "    ", filter, pkgPermissions);
 
                 pw.println("\n  Notification listeners:");
                 mListeners.dump(pw, filter);
@@ -6784,11 +6815,13 @@
 
 
         // blocked apps
+        boolean isMediaNotification = n.isMediaNotification()
+                && n.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
         boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
         synchronized (mNotificationLock) {
             isBlocked |= isRecordBlockedLocked(r);
         }
-        if (isBlocked) {
+        if (isBlocked && !isMediaNotification) {
             if (DBG) {
                 Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
                         + " by user request.");
@@ -7174,7 +7207,13 @@
                         return;
                     }
 
-                    if (appBanned || isRecordBlockedLocked(r)) {
+                    final StatusBarNotification n = r.getSbn();
+                    final Notification notification = n.getNotification();
+
+                    boolean isMediaNotification = notification.isMediaNotification()
+                            && notification.extras.getParcelable(
+                                    Notification.EXTRA_MEDIA_SESSION) != null;
+                    if (!isMediaNotification && (appBanned || isRecordBlockedLocked(r))) {
                         mUsageStats.registerBlocked(r);
                         if (DBG) {
                             Slog.e(TAG, "Suppressing notification from package " + pkg);
@@ -7189,8 +7228,6 @@
                         mUsageStats.registerSuspendedByAdmin(r);
                     }
                     NotificationRecord old = mNotificationsByKey.get(key);
-                    final StatusBarNotification n = r.getSbn();
-                    final Notification notification = n.getNotification();
 
                     // Make sure the SBN has an instance ID for statsd logging.
                     if (old == null || old.getSbn().getInstanceId() == null) {
@@ -7837,7 +7874,10 @@
                     final int waitMs = mAudioManager.getFocusRampTimeMs(
                             AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
                             record.getAudioAttributes());
-                    if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
+                    if (DBG) {
+                        Slog.v(TAG, "Delaying vibration for notification "
+                                + record.getKey() + " by " + waitMs + "ms");
+                    }
                     try {
                         Thread.sleep(waitMs);
                     } catch (InterruptedException e) { }
@@ -7845,9 +7885,17 @@
                     // so need to check the notification still valide for vibrate.
                     synchronized (mNotificationLock) {
                         if (mNotificationsByKey.get(record.getKey()) != null) {
-                            vibrate(record, effect, true);
+                            if (record.getKey().equals(mVibrateNotificationKey)) {
+                                vibrate(record, effect, true);
+                            } else {
+                                if (DBG) {
+                                    Slog.v(TAG, "No vibration for notification "
+                                            + record.getKey() + ": a new notification is "
+                                            + "vibrating, or effects were cleared while waiting");
+                                }
+                            }
                         } else {
-                            Slog.e(TAG, "No vibration for canceled notification : "
+                            Slog.w(TAG, "No vibration for canceled notification "
                                     + record.getKey());
                         }
                     }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 16fe4b6..cfa6ba5 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1931,31 +1931,41 @@
     }
 
     public void dump(PrintWriter pw, String prefix,
-            @NonNull NotificationManagerService.DumpFilter filter) {
+            @NonNull NotificationManagerService.DumpFilter filter,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
         pw.print(prefix);
         pw.println("per-package config version: " + XML_VERSION);
 
         pw.println("PackagePreferences:");
         synchronized (mPackagePreferences) {
-            dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
+            dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences, pkgPermissions);
         }
         pw.println("Restored without uid:");
-        dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
+        dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids, null);
     }
 
     public void dump(ProtoOutputStream proto,
-            @NonNull NotificationManagerService.DumpFilter filter) {
+            @NonNull NotificationManagerService.DumpFilter filter,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
         synchronized (mPackagePreferences) {
             dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
-                    mPackagePreferences);
+                    mPackagePreferences, pkgPermissions);
         }
         dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
-                mRestoredWithoutUids);
+                mRestoredWithoutUids, null);
     }
 
     private void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
             @NonNull NotificationManagerService.DumpFilter filter,
-            ArrayMap<String, PackagePreferences> packagePreferences) {
+            ArrayMap<String, PackagePreferences> packagePreferences,
+            ArrayMap<Pair<Integer, String>, Boolean> packagePermissions) {
+        // Used for tracking which package preferences we've seen already for notification
+        // permission reasons; after handling packages with local preferences, we'll want to dump
+        // the ones with notification permissions set but not local prefs.
+        Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+        if (packagePermissions != null) {
+            pkgsWithPermissionsToHandle = packagePermissions.keySet();
+        }
         final int N = packagePreferences.size();
         for (int i = 0; i < N; i++) {
             final PackagePreferences r = packagePreferences.valueAt(i);
@@ -1966,9 +1976,21 @@
                 pw.print(" (");
                 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
                 pw.print(')');
-                if (!mPermissionHelper.isMigrationEnabled() && r.importance != DEFAULT_IMPORTANCE) {
-                    pw.print(" importance=");
-                    pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
+                if (!mPermissionHelper.isMigrationEnabled()) {
+                    if (r.importance != DEFAULT_IMPORTANCE) {
+                        pw.print(" importance=");
+                        pw.print(NotificationListenerService.Ranking.importanceToString(
+                                r.importance));
+                    }
+                } else {
+                    Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                    if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+                        pw.print(" importance=");
+                        pw.print(NotificationListenerService.Ranking.importanceToString(
+                                packagePermissions.get(key)
+                                        ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+                        pkgsWithPermissionsToHandle.remove(key);
+                    }
                 }
                 if (r.priority != DEFAULT_PRIORITY) {
                     pw.print(" priority=");
@@ -2007,11 +2029,34 @@
                 }
             }
         }
+        // Handle any remaining packages with permissions
+        if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+            for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+                // p.first is the uid of this package; p.second is the package name
+                if (filter.matches(p.second)) {
+                    pw.print(prefix);
+                    pw.print("  AppSettings: ");
+                    pw.print(p.second);
+                    pw.print(" (");
+                    pw.print(p.first == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(p.first));
+                    pw.print(')');
+                    pw.print(" importance=");
+                    pw.print(NotificationListenerService.Ranking.importanceToString(
+                            packagePermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+                }
+            }
+        }
     }
 
-    private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
+    private void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
             @NonNull NotificationManagerService.DumpFilter filter,
-            ArrayMap<String, PackagePreferences> packagePreferences) {
+            ArrayMap<String, PackagePreferences> packagePreferences,
+            ArrayMap<Pair<Integer, String>, Boolean> packagePermissions) {
+        Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+        if (packagePermissions != null) {
+            pkgsWithPermissionsToHandle = packagePermissions.keySet();
+        }
+
         final int N = packagePreferences.size();
         long fToken;
         for (int i = 0; i < N; i++) {
@@ -2021,7 +2066,16 @@
 
                 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
                 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
-                proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
+                if (mPermissionHelper.isMigrationEnabled()) {
+                    Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                    if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+                        proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
+                                packagePermissions.get(key) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+                        pkgsWithPermissionsToHandle.remove(key);
+                    }
+                } else {
+                    proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
+                }
                 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
                 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
                 proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
@@ -2036,28 +2090,82 @@
                 proto.end(fToken);
             }
         }
+
+        if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+            for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+                if (filter.matches(p.second)) {
+                    fToken = proto.start(fieldId);
+                    proto.write(RankingHelperProto.RecordProto.PACKAGE, p.second);
+                    proto.write(RankingHelperProto.RecordProto.UID, p.first);
+                    proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
+                            packagePermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+                    proto.end(fToken);
+                }
+            }
+        }
     }
 
     /**
      * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
      */
-    public void pullPackagePreferencesStats(List<StatsEvent> events) {
+    public void pullPackagePreferencesStats(List<StatsEvent> events,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+        Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+        if (pkgPermissions != null) {
+            pkgsWithPermissionsToHandle = pkgPermissions.keySet();
+        }
+        int pulledEvents = 0;
         synchronized (mPackagePreferences) {
             for (int i = 0; i < mPackagePreferences.size(); i++) {
-                if (i > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
+                if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
                     break;
                 }
+                pulledEvents++;
                 SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
                         .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
                 final PackagePreferences r = mPackagePreferences.valueAt(i);
                 event.writeInt(r.uid);
                 event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
-                event.writeInt(r.importance);
+                if (mPermissionHelper.isMigrationEnabled()) {
+                    // Even if this package's data is not present, we need to write something;
+                    // so default to IMPORTANCE_NONE, since if PM doesn't know about the package
+                    // for some reason, notifications are not allowed.
+                    int importance = IMPORTANCE_NONE;
+                    Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                    if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+                        importance = pkgPermissions.get(key) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
+                        pkgsWithPermissionsToHandle.remove(key);
+                    }
+                    event.writeInt(importance);
+                } else {
+                    event.writeInt(r.importance);
+                }
                 event.writeInt(r.visibility);
                 event.writeInt(r.lockedAppFields);
                 events.add(event.build());
             }
         }
+
+        // handle remaining packages with PackageManager permissions but not local settings
+        if (mPermissionHelper.isMigrationEnabled() && pkgPermissions != null) {
+            for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+                if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
+                    break;
+                }
+                pulledEvents++;
+                SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
+                        .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
+                event.writeInt(p.first);
+                event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+                event.writeInt(pkgPermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+
+                // fill out the rest of the fields with default values so as not to confuse the
+                // builder
+                event.writeInt(DEFAULT_VISIBILITY);
+                event.writeInt(DEFAULT_LOCKED_APP_FIELDS);
+                events.add(event.build());
+            }
+        }
     }
 
     /**
@@ -2126,7 +2234,8 @@
         }
     }
 
-    public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
+    public JSONObject dumpJson(NotificationManagerService.DumpFilter filter,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
         JSONObject ranking = new JSONObject();
         JSONArray PackagePreferencess = new JSONArray();
         try {
@@ -2134,6 +2243,13 @@
         } catch (JSONException e) {
             // pass
         }
+
+        // Track data that we've handled from the permissions-based list
+        Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+        if (pkgPermissions != null) {
+            pkgsWithPermissionsToHandle = pkgPermissions.keySet();
+        }
+
         synchronized (mPackagePreferences) {
             final int N = mPackagePreferences.size();
             for (int i = 0; i < N; i++) {
@@ -2143,10 +2259,22 @@
                     try {
                         PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
                         PackagePreferences.put("packageName", r.pkg);
-                        if (r.importance != DEFAULT_IMPORTANCE) {
-                            PackagePreferences.put("importance",
-                                    NotificationListenerService.Ranking.importanceToString(
-                                            r.importance));
+                        if (mPermissionHelper.isMigrationEnabled()) {
+                            Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                            if (pkgPermissions != null
+                                    && pkgsWithPermissionsToHandle.contains(key)) {
+                                PackagePreferences.put("importance",
+                                        NotificationListenerService.Ranking.importanceToString(
+                                                pkgPermissions.get(key)
+                                                        ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+                                pkgsWithPermissionsToHandle.remove(key);
+                            }
+                        } else {
+                            if (r.importance != DEFAULT_IMPORTANCE) {
+                                PackagePreferences.put("importance",
+                                        NotificationListenerService.Ranking.importanceToString(
+                                                r.importance));
+                            }
                         }
                         if (r.priority != DEFAULT_PRIORITY) {
                             PackagePreferences.put("priority",
@@ -2176,6 +2304,27 @@
                 }
             }
         }
+
+        // handle packages for which there are permissions but no local settings
+        if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+            for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+                if (filter == null || filter.matches(p.second)) {
+                    JSONObject PackagePreferences = new JSONObject();
+                    try {
+                        PackagePreferences.put("userId", UserHandle.getUserId(p.first));
+                        PackagePreferences.put("packageName", p.second);
+                        PackagePreferences.put("importance",
+                                NotificationListenerService.Ranking.importanceToString(
+                                        pkgPermissions.get(p)
+                                                ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+                    } catch (JSONException e) {
+                        // pass
+                    }
+                    PackagePreferencess.put(PackagePreferences);
+                }
+            }
+        }
+
         try {
             ranking.put("PackagePreferencess", PackagePreferencess);
         } catch (JSONException e) {
@@ -2193,9 +2342,11 @@
      * @param filter
      * @return
      */
-    public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
+    public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter,
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
         JSONArray bans = new JSONArray();
-        Map<Integer, String> packageBans = getPackageBans();
+        Map<Integer, String> packageBans = mPermissionHelper.isMigrationEnabled()
+                ? getPermissionBasedPackageBans(pkgPermissions) : getPackageBans();
         for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
             final int userId = UserHandle.getUserId(ban.getKey());
             final String packageName = ban.getValue();
@@ -2228,6 +2379,21 @@
         }
     }
 
+    // Same functionality as getPackageBans by extracting the set of packages from the provided
+    // map that are disallowed from sending notifications.
+    protected Map<Integer, String> getPermissionBasedPackageBans(
+            ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+        ArrayMap<Integer, String> packageBans = new ArrayMap<>();
+        if (pkgPermissions != null) {
+            for (Pair<Integer, String> p : pkgPermissions.keySet()) {
+                if (!pkgPermissions.get(p)) {
+                    packageBans.put(p.first, p.second);
+                }
+            }
+        }
+        return packageBans;
+    }
+
     /**
      * Dump only the channel information as structured JSON for the stats collector.
      *
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 93c92c0..3f3cfd6 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1499,8 +1499,8 @@
             ai.primaryCpuAbi = ps.getPrimaryCpuAbi();
             ai.secondaryCpuAbi = ps.getSecondaryCpuAbi();
             ai.setVersionCode(ps.getVersionCode());
-            ai.flags = ps.getPkgFlags();
-            ai.privateFlags = ps.getPkgPrivateFlags();
+            ai.flags = ps.getFlags();
+            ai.privateFlags = ps.getPrivateFlags();
             pi.applicationInfo = PackageInfoWithoutStateUtils.generateDelegateApplicationInfo(
                     ai, flags, state, userId);
 
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b792a17..9306741 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -829,7 +829,7 @@
             }
             final String packageName = ps.getPkg().getPackageName();
             // Skip over if system app or static shared library
-            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
+            if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0
                     || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 239a9d71..fac73f5 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -16,13 +16,10 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
-import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
 import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
@@ -33,23 +30,18 @@
 import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
 import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
 import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
 
 import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.parsing.ParsingPackageUtils;
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.EventLog;
-import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.om.OverlayConfig;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
 import com.android.server.pm.parsing.PackageCacher;
@@ -69,13 +61,12 @@
  */
 final class InitAndSystemPackageHelper {
     private final PackageManagerService mPm;
-    private final RemovePackageHelper mRemovePackageHelper;
-    private final AppDataHelper mAppDataHelper;
 
     private final List<ScanPartition> mDirsToScanAsSystem;
     private final int mScanFlags;
     private final int mSystemParseFlags;
     private final int mSystemScanFlags;
+    private final InstallPackageHelper mInstallPackageHelper;
 
     /**
      * Tracks new system packages [received in an OTA] that we expect to
@@ -85,11 +76,9 @@
     private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
 
     // TODO(b/198166813): remove PMS dependency
-    InitAndSystemPackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
-            AppDataHelper appDataHelper) {
+    InitAndSystemPackageHelper(PackageManagerService pm) {
         mPm = pm;
-        mRemovePackageHelper = removePackageHelper;
-        mAppDataHelper = appDataHelper;
+        mInstallPackageHelper = new InstallPackageHelper(pm);
         mDirsToScanAsSystem = getSystemScanPartitions();
         // Set flag to monitor and not change apk file paths when scanning install directories.
         int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
@@ -158,11 +147,88 @@
                 consumer -> mPm.forEachPackage(
                         pkg -> consumer.accept(pkg, pkg.isSystem(),
                           apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
-        cleanupSystemPackagesAndInstallStubs(packageParser, executorService, packageSettings,
-                startTime, userIds);
+        // Prune any system packages that no longer exist.
+        final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
+        // Stub packages must either be replaced with full versions in the /data
+        // partition or be disabled.
+        final List<String> stubSystemApps = new ArrayList<>();
+
+        if (!mPm.isOnlyCoreApps()) {
+            // do this first before mucking with mPackages for the "expecting better" case
+            updateStubSystemAppsList(stubSystemApps);
+            mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
+                    possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
+        }
+
+        final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+
+        // Remove any shared userIDs that have no associated packages
+        mPm.mSettings.pruneSharedUsersLPw();
+        final long systemScanTime = SystemClock.uptimeMillis() - startTime;
+        final int systemPackagesCount = mPm.mPackages.size();
+        Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+                + " ms, packageCount: " + systemPackagesCount
+                + " , timePerPackage: "
+                + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+                + " , cached: " + cachedSystemApps);
+        if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
+            //CHECKSTYLE:OFF IndentationCheck
+            FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+                    BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
+                    systemScanTime / systemPackagesCount);
+            //CHECKSTYLE:ON IndentationCheck
+        }
+
+        if (!mPm.isOnlyCoreApps()) {
+            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
+                    SystemClock.uptimeMillis());
+            scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
+                    packageParser, executorService);
+
+        }
+
+        List<Runnable> unfinishedTasks = executorService.shutdownNow();
+        if (!unfinishedTasks.isEmpty()) {
+            throw new IllegalStateException("Not all tasks finished before calling close: "
+                    + unfinishedTasks);
+        }
+
+        if (!mPm.isOnlyCoreApps()) {
+            mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps,
+                    userIds, mScanFlags);
+            mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
+                    stubSystemApps, mSystemScanFlags, mSystemParseFlags);
+
+            // Uncompress and install any stubbed system applications.
+            // This must be done last to ensure all stubs are replaced or disabled.
+            mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags);
+
+            final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+                    - cachedSystemApps;
+
+            final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
+            final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
+            Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+                    + " ms, packageCount: " + dataPackagesCount
+                    + " , timePerPackage: "
+                    + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+                    + " , cached: " + cachedNonSystemApps);
+            if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
+                //CHECKSTYLE:OFF IndentationCheck
+                FrameworkStatsLog.write(
+                        FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+                        BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+                        dataScanTime / dataPackagesCount);
+                //CHECKSTYLE:OFF IndentationCheck
+            }
+        }
+        mExpectingBetter.clear();
+
+        mPm.mSettings.pruneRenamedPackagesLPw();
         packageParser.close();
         return overlayConfig;
     }
+
     /**
      * First part of init dir scanning
      */
@@ -205,255 +271,15 @@
         }
     }
 
-    /**
-     * Second part of init dir scanning
-     */
-    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    private void cleanupSystemPackagesAndInstallStubs(PackageParser2 packageParser,
-            ExecutorService executorService,
-            WatchedArrayMap<String, PackageSetting> packageSettings,
-            long startTime, int[] userIds) {
-        // Prune any system packages that no longer exist.
-        final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
-        // Stub packages must either be replaced with full versions in the /data
-        // partition or be disabled.
-        final List<String> stubSystemApps = new ArrayList<>();
-
-        if (!mPm.isOnlyCoreApps()) {
-            // do this first before mucking with mPackages for the "expecting better" case
-            final int numPackages = mPm.mPackages.size();
-            for (int index = 0; index < numPackages; index++) {
-                final AndroidPackage pkg = mPm.mPackages.valueAt(index);
-                if (pkg.isStub()) {
-                    stubSystemApps.add(pkg.getPackageName());
-                }
-            }
-
-            // Iterates PackageSettings in reversed order because the item could be removed
-            // during the iteration.
-            for (int index = packageSettings.size() - 1; index >= 0; index--) {
-                final PackageSetting ps = packageSettings.valueAt(index);
-
-                /*
-                 * If this is not a system app, it can't be a
-                 * disable system app.
-                 */
-                if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                    continue;
-                }
-
-                /*
-                 * If the package is scanned, it's not erased.
-                 */
-                final AndroidPackage scannedPkg = mPm.mPackages.get(ps.getPackageName());
-                final PackageSetting disabledPs =
-                        mPm.mSettings.getDisabledSystemPkgLPr(ps.getPackageName());
-                if (scannedPkg != null) {
-                    /*
-                     * If the system app is both scanned and in the
-                     * disabled packages list, then it must have been
-                     * added via OTA. Remove it from the currently
-                     * scanned package so the previously user-installed
-                     * application can be scanned.
-                     */
-                    if (disabledPs != null) {
-                        logCriticalInfo(Log.WARN,
-                                "Expecting better updated system app for "
-                                        + ps.getPackageName()
-                                        + "; removing system app.  Last known"
-                                        + " codePath=" + ps.getPathString()
-                                        + ", versionCode=" + ps.getVersionCode()
-                                        + "; scanned versionCode="
-                                        + scannedPkg.getLongVersionCode());
-                        mRemovePackageHelper.removePackageLI(scannedPkg, true);
-                        mExpectingBetter.put(ps.getPackageName(), ps.getPath());
-                    }
-
-                    continue;
-                }
-
-                if (disabledPs == null) {
-                    logCriticalInfo(Log.WARN, "System package " + ps.getPackageName()
-                            + " no longer exists; its data will be wiped");
-                    mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
-                } else {
-                    // we still have a disabled system package, but, it still might have
-                    // been removed. check the code path still exists and check there's
-                    // still a package. the latter can happen if an OTA keeps the same
-                    // code path, but, changes the package name.
-                    if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
-                            || disabledPs.getPkg() == null) {
-                        possiblyDeletedUpdatedSystemApps.add(ps.getPackageName());
-                    } else {
-                        // We're expecting that the system app should remain disabled, but add
-                        // it to expecting better to recover in case the data version cannot
-                        // be scanned.
-                        mExpectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
-                    }
-                }
+    @GuardedBy("mPm.mLock")
+    private void updateStubSystemAppsList(List<String> stubSystemApps) {
+        final int numPackages = mPm.mPackages.size();
+        for (int index = 0; index < numPackages; index++) {
+            final AndroidPackage pkg = mPm.mPackages.valueAt(index);
+            if (pkg.isStub()) {
+                stubSystemApps.add(pkg.getPackageName());
             }
         }
-
-        final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
-
-        // Remove any shared userIDs that have no associated packages
-        mPm.mSettings.pruneSharedUsersLPw();
-        final long systemScanTime = SystemClock.uptimeMillis() - startTime;
-        final int systemPackagesCount = mPm.mPackages.size();
-        Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
-                + " ms, packageCount: " + systemPackagesCount
-                + " , timePerPackage: "
-                + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
-                + " , cached: " + cachedSystemApps);
-        if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
-            //CHECKSTYLE:OFF IndentationCheck
-            FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
-                    BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
-                    systemScanTime / systemPackagesCount);
-            //CHECKSTYLE:ON IndentationCheck
-        }
-        if (!mPm.isOnlyCoreApps()) {
-            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
-                    SystemClock.uptimeMillis());
-            scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
-                    packageParser, executorService);
-
-        }
-
-        List<Runnable> unfinishedTasks = executorService.shutdownNow();
-        if (!unfinishedTasks.isEmpty()) {
-            throw new IllegalStateException("Not all tasks finished before calling close: "
-                    + unfinishedTasks);
-        }
-
-        if (!mPm.isOnlyCoreApps()) {
-            final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
-            // 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.
-            for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
-                final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
-                final AndroidPackage pkg = mPm.mPackages.get(packageName);
-                final String msg;
-
-                // remove from the disabled system list; do this first so any future
-                // scans of this package are performed without this state
-                mPm.mSettings.removeDisabledSystemPackageLPw(packageName);
-
-                if (pkg == null) {
-                    // should have found an update, but, we didn't; remove everything
-                    msg = "Updated system package " + packageName
-                            + " no longer exists; removing its data";
-                    // Actual deletion of code and data will be handled by later
-                    // reconciliation step
-                } else {
-                    // found an update; revoke system privileges
-                    msg = "Updated system package " + packageName
-                            + " no longer exists; rescanning package on data";
-
-                    // NOTE: We don't do anything special if a stub is removed from the
-                    // system image. But, if we were [like removing the uncompressed
-                    // version from the /data partition], this is where it'd be done.
-
-                    // remove the package from the system and re-scan it without any
-                    // special privileges
-                    mRemovePackageHelper.removePackageLI(pkg, true);
-                    try {
-                        final File codePath = new File(pkg.getPath());
-                        scanPackageHelper.scanPackageTracedLI(codePath, 0, mScanFlags, 0, null);
-                    } catch (PackageManagerException e) {
-                        Slog.e(TAG, "Failed to parse updated, ex-system package: "
-                                + e.getMessage());
-                    }
-                }
-
-                // one final check. if we still have a package setting [ie. it was
-                // previously scanned and known to the system], but, we don't have
-                // a package [ie. there was an error scanning it from the /data
-                // partition], completely remove the package data.
-                final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
-                if (ps != null && mPm.mPackages.get(packageName) == null) {
-                    mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
-
-                }
-                logCriticalInfo(Log.WARN, msg);
-            }
-
-            /*
-             * Make sure all system apps that we expected to appear on
-             * the userdata partition actually showed up. If they never
-             * appeared, crawl back and revive the system version.
-             */
-            for (int i = 0; i < mExpectingBetter.size(); i++) {
-                final String packageName = mExpectingBetter.keyAt(i);
-                if (!mPm.mPackages.containsKey(packageName)) {
-                    final File scanFile = mExpectingBetter.valueAt(i);
-
-                    logCriticalInfo(Log.WARN, "Expected better " + packageName
-                            + " but never showed up; reverting to system");
-
-                    @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
-                    @PackageManagerService.ScanFlags int rescanFlags = 0;
-                    for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
-                        final ScanPartition partition = mDirsToScanAsSystem.get(i1);
-                        if (partition.containsPrivApp(scanFile)) {
-                            reparseFlags = mSystemParseFlags;
-                            rescanFlags = mSystemScanFlags | SCAN_AS_PRIVILEGED
-                                    | partition.scanFlag;
-                            break;
-                        }
-                        if (partition.containsApp(scanFile)) {
-                            reparseFlags = mSystemParseFlags;
-                            rescanFlags = mSystemScanFlags | partition.scanFlag;
-                            break;
-                        }
-                    }
-                    if (rescanFlags == 0) {
-                        Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
-                        continue;
-                    }
-                    mPm.mSettings.enableSystemPackageLPw(packageName);
-
-                    try {
-                        final AndroidPackage newPkg = scanPackageHelper.scanPackageTracedLI(
-                                scanFile, reparseFlags, rescanFlags, 0, null);
-                        // We rescanned a stub, add it to the list of stubbed system packages
-                        if (newPkg.isStub()) {
-                            stubSystemApps.add(packageName);
-                        }
-                    } catch (PackageManagerException e) {
-                        Slog.e(TAG, "Failed to parse original system package: "
-                                + e.getMessage());
-                    }
-                }
-            }
-
-            // Uncompress and install any stubbed system applications.
-            // This must be done last to ensure all stubs are replaced or disabled.
-            new InstallPackageHelper(mPm).installSystemStubPackages(stubSystemApps, mScanFlags);
-
-            final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
-                    - cachedSystemApps;
-
-            final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
-            final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
-            Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
-                    + " ms, packageCount: " + dataPackagesCount
-                    + " , timePerPackage: "
-                    + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
-                    + " , cached: " + cachedNonSystemApps);
-            if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
-                //CHECKSTYLE:OFF IndentationCheck
-                FrameworkStatsLog.write(
-                        FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
-                        BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
-                        dataScanTime / dataPackagesCount);
-                //CHECKSTYLE:OFF IndentationCheck
-            }
-        }
-        mExpectingBetter.clear();
-
-        mPm.mSettings.pruneRenamedPackagesLPw();
     }
 
     @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
@@ -461,89 +287,13 @@
             long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
         try {
-            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
+            mInstallPackageHelper.installSystemPackagesFromDir(scanDir, parseFlags, scanFlags,
+                    currentTime, packageParser, executorService);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
-    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
-            PackageParser2 packageParser, ExecutorService executorService) {
-        final File[] files = scanDir.listFiles();
-        if (ArrayUtils.isEmpty(files)) {
-            Log.d(TAG, "No files in app dir " + scanDir);
-            return;
-        }
-
-        if (DEBUG_PACKAGE_SCANNING) {
-            Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
-                    + " flags=0x" + Integer.toHexString(parseFlags));
-        }
-
-        ParallelPackageParser parallelPackageParser =
-                new ParallelPackageParser(packageParser, executorService);
-
-        // Submit files for parsing in parallel
-        int fileCount = 0;
-        for (File file : files) {
-            final boolean isPackage = (isApkFile(file) || file.isDirectory())
-                    && !PackageInstallerService.isStageName(file.getName());
-            if (!isPackage) {
-                // Ignore entries which are not packages
-                continue;
-            }
-            parallelPackageParser.submit(file, parseFlags);
-            fileCount++;
-        }
-
-        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
-        // Process results one by one
-        for (; fileCount > 0; fileCount--) {
-            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
-            Throwable throwable = parseResult.throwable;
-            int errorCode = PackageManager.INSTALL_SUCCEEDED;
-            String errorMsg = null;
-
-            if (throwable == null) {
-                // TODO(b/194319951): move lower in the scan chain
-                // Static shared libraries have synthetic package names
-                if (parseResult.parsedPackage.isStaticSharedLibrary()) {
-                    PackageManagerService.renameStaticSharedLibraryPackage(
-                            parseResult.parsedPackage);
-                }
-                try {
-                    scanPackageHelper.addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
-                            currentTime, null);
-                } catch (PackageManagerException e) {
-                    errorCode = e.error;
-                    errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
-                    Slog.w(TAG, errorMsg);
-                }
-            } else if (throwable instanceof PackageManagerException) {
-                PackageManagerException e = (PackageManagerException) throwable;
-                errorCode = e.error;
-                errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
-                Slog.w(TAG, errorMsg);
-            } else {
-                throw new IllegalStateException("Unexpected exception occurred while parsing "
-                        + parseResult.scanFile, throwable);
-            }
-
-            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
-                mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
-            }
-
-            // Delete invalid userdata apps
-            if ((scanFlags & SCAN_AS_SYSTEM) == 0
-                    && errorCode != PackageManager.INSTALL_SUCCEEDED) {
-                logCriticalInfo(Log.WARN,
-                        "Deleting invalid package at " + parseResult.scanFile);
-                mRemovePackageHelper.removeCodePathLI(parseResult.scanFile);
-            }
-        }
-    }
-
     public boolean isExpectingBetter(String packageName) {
         return mExpectingBetter.containsKey(packageName);
     }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8ba8e45..2e5a6ea 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -36,8 +36,10 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
 import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -159,9 +161,9 @@
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.pm.permission.Permission;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
-import com.android.server.pm.pkg.PackageUserState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.rollback.RollbackManagerInternal;
+import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 
 import dalvik.system.VMRuntime;
@@ -185,6 +187,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 
 final class InstallPackageHelper {
     /**
@@ -196,6 +199,8 @@
     private final AppDataHelper mAppDataHelper;
     private final PackageManagerServiceInjector mInjector;
     private final BroadcastHelper mBroadcastHelper;
+    private final RemovePackageHelper mRemovePackageHelper;
+    private final ScanPackageHelper mScanPackageHelper;
 
     // TODO(b/198166813): remove PMS dependency
     InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
@@ -203,6 +208,8 @@
         mInjector = pm.mInjector;
         mAppDataHelper = appDataHelper;
         mBroadcastHelper = new BroadcastHelper(mInjector);
+        mRemovePackageHelper = new RemovePackageHelper(pm);
+        mScanPackageHelper = new ScanPackageHelper(pm);
     }
 
     InstallPackageHelper(PackageManagerService pm) {
@@ -214,6 +221,8 @@
         mInjector = injector;
         mAppDataHelper = new AppDataHelper(pm, mInjector);
         mBroadcastHelper = new BroadcastHelper(injector);
+        mRemovePackageHelper = new RemovePackageHelper(pm);
+        mScanPackageHelper = new ScanPackageHelper(pm);
     }
 
     @GuardedBy("mPm.mLock")
@@ -1132,7 +1141,6 @@
                 new ArrayMap<>(requests.size());
         final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
         boolean success = false;
-        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
             for (InstallRequest request : requests) {
@@ -1161,7 +1169,7 @@
                 installResults.put(packageName, request.mInstallResult);
                 installArgs.put(packageName, request.mArgs);
                 try {
-                    final ScanResult result = scanPackageHelper.scanPackageTracedLI(
+                    final ScanResult result = mScanPackageHelper.scanPackageTracedLI(
                             prepareResult.mPackageToScan, prepareResult.mParseFlags,
                             prepareResult.mScanFlags, System.currentTimeMillis(),
                             request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -1174,8 +1182,7 @@
                                         + " in multi-package install request.");
                         return;
                     }
-                    createdAppId.put(packageName,
-                            scanPackageHelper.optimisticallyRegisterAppId(result));
+                    createdAppId.put(packageName, optimisticallyRegisterAppId(result));
                     versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
                             mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
                     if (result.mStaticSharedLibraryInfo != null) {
@@ -1253,7 +1260,7 @@
                 for (ScanResult result : preparedScans.values()) {
                     if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
                             false)) {
-                        scanPackageHelper.cleanUpAppIdCreation(result);
+                        cleanUpAppIdCreation(result);
                     }
                 }
                 // TODO(b/194319951): create a more descriptive reason than unknown
@@ -2185,15 +2192,16 @@
                             reconciledPkg.mPrepareResult.mExistingPackage.getPackageName());
                     if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP)
                             == 0) {
-                        if (ps1.mOldCodePaths == null) {
-                            ps1.mOldCodePaths = new ArraySet<>();
+                        if (ps1.getOldCodePaths() == null) {
+                            ps1.setOldCodePaths(new ArraySet<>());
                         }
-                        Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
+                        Collections.addAll(ps1.getOldCodePaths(), oldPackage.getBaseApkPath());
                         if (oldPackage.getSplitCodePaths() != null) {
-                            Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
+                            Collections.addAll(ps1.getOldCodePaths(),
+                                    oldPackage.getSplitCodePaths());
                         }
                     } else {
-                        ps1.mOldCodePaths = null;
+                        ps1.setOldCodePaths(null);
                     }
 
                     if (reconciledPkg.mInstallResult.mReturnCode
@@ -3357,9 +3365,8 @@
         }
         final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
         removePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
-        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
         try {
-            return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+            return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
                     e);
@@ -3509,9 +3516,7 @@
                         | ParsingPackageUtils.PARSE_MUST_BE_APK
                         | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
         @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
-        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
-        final AndroidPackage pkg =
-                scanPackageHelper.scanPackageTracedLI(
+        final AndroidPackage pkg = scanSystemPackageTracedLI(
                         codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
 
         PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3582,4 +3587,388 @@
         }
     }
 
+    @GuardedBy("mPm.mLock")
+    public void prepareSystemPackageCleanUp(
+            WatchedArrayMap<String, PackageSetting> packageSettings,
+            List<String> possiblyDeletedUpdatedSystemApps,
+            ArrayMap<String, File> expectingBetter, int[] userIds) {
+        // Iterates PackageSettings in reversed order because the item could be removed
+        // during the iteration.
+        for (int index = packageSettings.size() - 1; index >= 0; index--) {
+            final PackageSetting ps = packageSettings.valueAt(index);
+            final String packageName = ps.getPackageName();
+            /*
+             * If this is not a system app, it can't be a
+             * disable system app.
+             */
+            if (!ps.isSystem()) {
+                continue;
+            }
+
+            /*
+             * If the package is scanned, it's not erased.
+             */
+            final AndroidPackage scannedPkg = mPm.mPackages.get(packageName);
+            final PackageSetting disabledPs =
+                    mPm.mSettings.getDisabledSystemPkgLPr(packageName);
+            if (scannedPkg != null) {
+                /*
+                 * If the system app is both scanned and in the
+                 * disabled packages list, then it must have been
+                 * added via OTA. Remove it from the currently
+                 * scanned package so the previously user-installed
+                 * application can be scanned.
+                 */
+                if (disabledPs != null) {
+                    logCriticalInfo(Log.WARN,
+                            "Expecting better updated system app for "
+                                    + packageName
+                                    + "; removing system app.  Last known"
+                                    + " codePath=" + ps.getPathString()
+                                    + ", versionCode=" + ps.getVersionCode()
+                                    + "; scanned versionCode="
+                                    + scannedPkg.getLongVersionCode());
+                    mRemovePackageHelper.removePackageLI(scannedPkg, true);
+                    expectingBetter.put(ps.getPackageName(), ps.getPath());
+                }
+
+                continue;
+            }
+
+            if (disabledPs == null) {
+                logCriticalInfo(Log.WARN, "System package " + packageName
+                        + " no longer exists; its data will be wiped");
+                mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
+            } else {
+                // we still have a disabled system package, but, it still might have
+                // been removed. check the code path still exists and check there's
+                // still a package. the latter can happen if an OTA keeps the same
+                // code path, but, changes the package name.
+                if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
+                        || disabledPs.getPkg() == null) {
+                    possiblyDeletedUpdatedSystemApps.add(packageName);
+                } else {
+                    // We're expecting that the system app should remain disabled, but add
+                    // it to expecting better to recover in case the data version cannot
+                    // be scanned.
+                    expectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mPm.mLock")
+    // 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.
+    public void cleanupDisabledPackageSettings(List<String> possiblyDeletedUpdatedSystemApps,
+            int[] userIds, int scanFlags) {
+        for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
+            final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
+            final AndroidPackage pkg = mPm.mPackages.get(packageName);
+            final String msg;
+
+            // remove from the disabled system list; do this first so any future
+            // scans of this package are performed without this state
+            mPm.mSettings.removeDisabledSystemPackageLPw(packageName);
+
+            if (pkg == null) {
+                // should have found an update, but, we didn't; remove everything
+                msg = "Updated system package " + packageName
+                        + " no longer exists; removing its data";
+                // Actual deletion of code and data will be handled by later
+                // reconciliation step
+            } else {
+                // found an update; revoke system privileges
+                msg = "Updated system package " + packageName
+                        + " no longer exists; rescanning package on data";
+
+                // NOTE: We don't do anything special if a stub is removed from the
+                // system image. But, if we were [like removing the uncompressed
+                // version from the /data partition], this is where it'd be done.
+
+                // remove the package from the system and re-scan it without any
+                // special privileges
+                mRemovePackageHelper.removePackageLI(pkg, true);
+                try {
+                    final File codePath = new File(pkg.getPath());
+                    scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null);
+                } catch (PackageManagerException e) {
+                    Slog.e(TAG, "Failed to parse updated, ex-system package: "
+                            + e.getMessage());
+                }
+            }
+
+            // one final check. if we still have a package setting [ie. it was
+            // previously scanned and known to the system], but, we don't have
+            // a package [ie. there was an error scanning it from the /data
+            // partition], completely remove the package data.
+            final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+            if (ps != null && mPm.mPackages.get(packageName) == null) {
+                mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
+            }
+            logCriticalInfo(Log.WARN, msg);
+        }
+    }
+
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    public void installSystemPackagesFromDir(File scanDir, int parseFlags, int scanFlags,
+            long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
+        final File[] files = scanDir.listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            Log.d(TAG, "No files in app dir " + scanDir);
+            return;
+        }
+
+        if (DEBUG_PACKAGE_SCANNING) {
+            Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+                    + " flags=0x" + Integer.toHexString(parseFlags));
+        }
+        ParallelPackageParser parallelPackageParser =
+                new ParallelPackageParser(packageParser, executorService);
+
+        // Submit files for parsing in parallel
+        int fileCount = 0;
+        for (File file : files) {
+            final boolean isPackage = (isApkFile(file) || file.isDirectory())
+                    && !PackageInstallerService.isStageName(file.getName());
+            if (!isPackage) {
+                // Ignore entries which are not packages
+                continue;
+            }
+            parallelPackageParser.submit(file, parseFlags);
+            fileCount++;
+        }
+
+        // Process results one by one
+        for (; fileCount > 0; fileCount--) {
+            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
+            Throwable throwable = parseResult.throwable;
+            int errorCode = PackageManager.INSTALL_SUCCEEDED;
+            String errorMsg = null;
+
+            if (throwable == null) {
+                // TODO(b/194319951): move lower in the scan chain
+                // Static shared libraries have synthetic package names
+                if (parseResult.parsedPackage.isStaticSharedLibrary()) {
+                    PackageManagerService.renameStaticSharedLibraryPackage(
+                            parseResult.parsedPackage);
+                }
+                try {
+                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime,
+                            null);
+                } catch (PackageManagerException e) {
+                    errorCode = e.error;
+                    errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
+                    Slog.w(TAG, errorMsg);
+                }
+            } else if (throwable instanceof PackageManagerException) {
+                PackageManagerException e = (PackageManagerException) throwable;
+                errorCode = e.error;
+                errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
+                Slog.w(TAG, errorMsg);
+            } else {
+                throw new IllegalStateException("Unexpected exception occurred while parsing "
+                        + parseResult.scanFile, throwable);
+            }
+
+            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
+                mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
+            }
+
+            // Delete invalid userdata apps
+            if ((scanFlags & SCAN_AS_SYSTEM) == 0
+                    && errorCode != PackageManager.INSTALL_SUCCEEDED) {
+                logCriticalInfo(Log.WARN,
+                        "Deleting invalid package at " + parseResult.scanFile);
+                mRemovePackageHelper.removeCodePathLI(parseResult.scanFile);
+            }
+        }
+    }
+
+    /**
+     * Make sure all system apps that we expected to appear on
+     * the userdata partition actually showed up. If they never
+     * appeared, crawl back and revive the system version.
+     */
+    @GuardedBy("mPm.mLock")
+    public void checkExistingBetterPackages(ArrayMap<String, File> expectingBetterPackages,
+            List<String> stubSystemApps, int systemScanFlags, int systemParseFlags) {
+        for (int i = 0; i < expectingBetterPackages.size(); i++) {
+            final String packageName = expectingBetterPackages.keyAt(i);
+            if (mPm.mPackages.containsKey(packageName)) {
+                continue;
+            }
+            final File scanFile = expectingBetterPackages.valueAt(i);
+
+            logCriticalInfo(Log.WARN, "Expected better " + packageName
+                    + " but never showed up; reverting to system");
+
+            final Pair<Integer, Integer> rescanAndReparseFlags =
+                    mPm.getSystemPackageRescanFlagsAndReparseFlags(scanFile,
+                            systemScanFlags, systemParseFlags);
+            @PackageManagerService.ScanFlags int rescanFlags = rescanAndReparseFlags.first;
+            @ParsingPackageUtils.ParseFlags int reparseFlags = rescanAndReparseFlags.second;
+
+            if (rescanFlags == 0) {
+                Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
+                continue;
+            }
+            mPm.mSettings.enableSystemPackageLPw(packageName);
+
+            try {
+                final AndroidPackage newPkg = scanSystemPackageTracedLI(
+                        scanFile, reparseFlags, rescanFlags, 0, null);
+                // We rescanned a stub, add it to the list of stubbed system packages
+                if (newPkg.isStub()) {
+                    stubSystemApps.add(packageName);
+                }
+            } catch (PackageManagerException e) {
+                Slog.e(TAG, "Failed to parse original system package: "
+                        + e.getMessage());
+            }
+        }
+    }
+
+    /**
+     *  Traces a package scan.
+     *  @see #scanSystemPackageLI(File, int, int, long, UserHandle)
+     */
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
+            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
+        try {
+            return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    /**
+     *  Scans a package and returns the newly parsed package.
+     *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
+     */
+    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+    private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
+            long currentTime, UserHandle user) throws PackageManagerException {
+        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
+        final ParsedPackage parsedPackage;
+        try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
+            parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+
+        // Static shared libraries have synthetic package names
+        if (parsedPackage.isStaticSharedLibrary()) {
+            PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+        }
+
+        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
+    }
+
+    /**
+     * Adds a new package to the internal data structures during platform initialization.
+     * <p>After adding, the package is known to the system and available for querying.
+     * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
+     * etc...], additional checks are performed. Basic verification [such as ensuring
+     * matching signatures, checking version codes, etc...] occurs if the package is
+     * identical to a previously known package. If the package fails a signature check,
+     * the version installed on /data will be removed. If the version of the new package
+     * is less than or equal than the version on /data, it will be ignored.
+     * <p>Regardless of the package location, the results are applied to the internal
+     * structures and the package is made available to the rest of the system.
+     * <p>NOTE: The return value should be removed. It's the passed in package object.
+     */
+    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+    private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
+            @ParsingPackageUtils.ParseFlags int parseFlags,
+            @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+            @Nullable UserHandle user) throws PackageManagerException {
+
+        final Pair<ScanResult, Boolean> scanResultPair = mScanPackageHelper.scanSystemPackageLI(
+                parsedPackage, parseFlags, scanFlags, currentTime, user);
+        final ScanResult scanResult = scanResultPair.first;
+        boolean shouldHideSystemApp = scanResultPair.second;
+        if (scanResult.mSuccess) {
+            synchronized (mPm.mLock) {
+                boolean appIdCreated = false;
+                try {
+                    final String pkgName = scanResult.mPkgSetting.getPackageName();
+                    final Map<String, ReconciledPackage> reconcileResult =
+                            reconcilePackagesLocked(
+                                    new ReconcileRequest(
+                                            Collections.singletonMap(pkgName, scanResult),
+                                            mPm.mSharedLibraries,
+                                            mPm.mPackages,
+                                            Collections.singletonMap(
+                                                    pkgName,
+                                                    mPm.getSettingsVersionForPackage(
+                                                            parsedPackage)),
+                                            Collections.singletonMap(pkgName,
+                                                    mPm.getSharedLibLatestVersionSetting(
+                                                            scanResult))),
+                                    mPm.mSettings.getKeySetManagerService(), mInjector);
+                    appIdCreated = optimisticallyRegisterAppId(scanResult);
+                    commitReconciledScanResultLocked(
+                            reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+                } catch (PackageManagerException e) {
+                    if (appIdCreated) {
+                        cleanUpAppIdCreation(scanResult);
+                    }
+                    throw e;
+                }
+            }
+        }
+
+        if (shouldHideSystemApp) {
+            synchronized (mPm.mLock) {
+                mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
+            }
+        }
+        if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+            if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isPackageLoading()) {
+                // Continue monitoring loading progress of active incremental packages
+                mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+                        new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
+            }
+        }
+        return scanResult.mPkgSetting.getPkg();
+    }
+
+    /**
+     * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
+     * the app ID required for reconcile.
+     * @return {@code true} if a new app ID was registered and will need to be cleaned up on
+     *         failure.
+     */
+    private boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
+            throws PackageManagerException {
+        if (!result.mExistingSettingCopied || result.needsNewAppId()) {
+            synchronized (mPm.mLock) {
+                // THROWS: when we can't allocate a user id. add call to check if there's
+                // enough space to ensure we won't throw; otherwise, don't modify state
+                return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.needsNewAppId());
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Reverts any app ID creation that were made by
+     * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
+     * referenced method returned true.
+     */
+    private void cleanUpAppIdCreation(@NonNull ScanResult result) {
+        // iff we've acquired an app ID for a new package setting, remove it so that it can be
+        // acquired by another request.
+        if (result.mPkgSetting.getAppId() > 0) {
+            mPm.mSettings.removeAppIdLPw(result.mPkgSetting.getAppId());
+        }
+    }
+
+
 }
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 0dff7d1..d996fe4 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -169,13 +169,15 @@
             final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
                     mOriginInfo.mResolvedPath, mPackageAbiOverride);
             if (sizeBytes >= 0) {
-                try {
-                    mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
-                    pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
-                            mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
-                            mPackageAbiOverride);
-                } catch (Installer.InstallerException e) {
-                    Slog.w(TAG, "Failed to free cache", e);
+                synchronized (mPm.mInstallLock) {
+                    try {
+                        mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
+                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
+                                mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
+                                mPackageAbiOverride);
+                    } catch (Installer.InstallerException e) {
+                        Slog.w(TAG, "Failed to free cache", e);
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 55355d8..c8bd2c0 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -632,11 +632,15 @@
         }
     }
 
-    public void freeCache(String uuid, long targetFreeBytes, long cacheReservedBytes, int flags)
-            throws InstallerException {
+    /**
+     * Deletes cache from specified uuid until targetFreeBytes amount of space is free.
+     * flag denotes aggressive or non-aggresive mode where cache under quota is eligible or not
+     * respectively for clearing.
+     */
+    public void freeCache(String uuid, long targetFreeBytes, int flags) throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.freeCache(uuid, targetFreeBytes, cacheReservedBytes, flags);
+            mInstalld.freeCache(uuid, targetFreeBytes, flags);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index e845aec..a5b42f0 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -46,6 +46,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.SharedLibraryInfo;
@@ -65,6 +66,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.F2fsUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.apphibernation.AppHibernationManagerInternal;
@@ -141,6 +143,7 @@
     private final Injector mInjector;
 
 
+    private final Context mContext;
     private static final Random sRandom = new Random();
 
     PackageDexOptimizer(Installer installer, Object installLock, Context context,
@@ -159,6 +162,7 @@
     }
 
     protected PackageDexOptimizer(PackageDexOptimizer from) {
+        this.mContext = from.mContext;
         this.mInstaller = from.mInstaller;
         this.mInstallLock = from.mInstallLock;
         this.mDexoptWakeLock = from.mDexoptWakeLock;
@@ -169,6 +173,7 @@
     @VisibleForTesting
     PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock,
             Context context, String wakeLockTag) {
+        this.mContext = context;
         this.mInstaller = installer;
         this.mInstallLock = installLock;
 
@@ -434,6 +439,13 @@
                 long endTime = System.currentTimeMillis();
                 packageStats.setCompileTime(path, (int)(endTime - startTime));
             }
+            if (oatDir != null) {
+                // Release odex/vdex compressed blocks to save user space.
+                // Compression support will be checked in F2fsUtils.
+                // The system app may be dexed, oatDir may be null, skip this situation.
+                final ContentResolver resolver = mContext.getContentResolver();
+                F2fsUtils.releaseCompressedBlocks(resolver, new File(oatDir));
+            }
             return DEX_OPT_PERFORMED;
         } catch (InstallerException e) {
             Slog.w(TAG, "Failed to dexopt", e);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fc266c8..de1c2ad 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -107,9 +107,13 @@
 import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Random;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.function.IntPredicate;
 import java.util.function.Supplier;
 
@@ -1436,13 +1440,74 @@
         }
     }
 
-    void dump(IndentingPrintWriter pw) {
-        synchronized (mSessions) {
-            pw.println("Active install sessions:");
+    static class ParentChildSessionMap {
+        private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap;
+
+        private final Comparator<PackageInstallerSession> mSessionCreationComparator =
+                Comparator.comparingLong((PackageInstallerSession sess) -> sess.createdMillis)
+                          .thenComparingInt(sess -> sess.sessionId);
+
+        ParentChildSessionMap() {
+            mSessionMap = new TreeMap<>(mSessionCreationComparator);
+        }
+
+        boolean containsSession() {
+            return !(mSessionMap.isEmpty());
+        }
+
+        private void addParentSession(PackageInstallerSession session) {
+            if (!mSessionMap.containsKey(session)) {
+                mSessionMap.put(session, new TreeSet<>(mSessionCreationComparator));
+            }
+        }
+
+        private void addChildSession(PackageInstallerSession session,
+                PackageInstallerSession parentSession) {
+            addParentSession(parentSession);
+            mSessionMap.get(parentSession).add(session);
+        }
+
+        void addSession(PackageInstallerSession session,
+                PackageInstallerSession parentSession) {
+            if (session.hasParentSessionId()) {
+                addChildSession(session, parentSession);
+            } else {
+                addParentSession(session);
+            }
+        }
+
+        void dump(String tag, IndentingPrintWriter pw) {
+            pw.println(tag + " install sessions:");
             pw.increaseIndent();
 
-            List<PackageInstallerSession> finalizedSessions = new ArrayList<>();
-            List<PackageInstallerSession> orphanedChildSessions = new ArrayList<>();
+            for (Map.Entry<PackageInstallerSession, TreeSet<PackageInstallerSession>> entry
+                    : mSessionMap.entrySet()) {
+                PackageInstallerSession parentSession = entry.getKey();
+                pw.print(tag + " ");
+                parentSession.dump(pw);
+                pw.println();
+                pw.increaseIndent();
+
+                for (PackageInstallerSession childSession : entry.getValue()) {
+                    pw.print(tag + " Child ");
+                    childSession.dump(pw);
+                    pw.println();
+                }
+
+                pw.decreaseIndent();
+            }
+
+            pw.println();
+            pw.decreaseIndent();
+        }
+    }
+
+    void dump(IndentingPrintWriter pw) {
+        synchronized (mSessions) {
+            ParentChildSessionMap activeSessionMap = new ParentChildSessionMap();
+            ParentChildSessionMap orphanedChildSessionMap = new ParentChildSessionMap();
+            ParentChildSessionMap finalizedSessionMap = new ParentChildSessionMap();
+
             int N = mSessions.size();
             for (int i = 0; i < N; i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
@@ -1452,47 +1517,28 @@
                         : session;
                 // Do not print orphaned child sessions as active install sessions
                 if (rootSession == null) {
-                    orphanedChildSessions.add(session);
+                    orphanedChildSessionMap.addSession(session, rootSession);
                     continue;
                 }
 
                 // Do not print finalized staged session as active install sessions
                 if (rootSession.isStagedAndInTerminalState()) {
-                    finalizedSessions.add(session);
+                    finalizedSessionMap.addSession(session, rootSession);
                     continue;
                 }
 
-                session.dump(pw);
-                pw.println();
+                activeSessionMap.addSession(session, rootSession);
             }
-            pw.println();
-            pw.decreaseIndent();
 
-            if (!orphanedChildSessions.isEmpty()) {
+            activeSessionMap.dump("Active", pw);
+
+            if (orphanedChildSessionMap.containsSession()) {
                 // Presence of orphaned sessions indicate leak in cleanup for multi-package and
                 // should be cleaned up.
-                pw.println("Orphaned install sessions:");
-                pw.increaseIndent();
-                N = orphanedChildSessions.size();
-                for (int i = 0; i < N; i++) {
-                    final PackageInstallerSession session = orphanedChildSessions.get(i);
-                    session.dump(pw);
-                    pw.println();
-                }
-                pw.println();
-                pw.decreaseIndent();
+                orphanedChildSessionMap.dump("Orphaned", pw);
             }
 
-            pw.println("Finalized install sessions:");
-            pw.increaseIndent();
-            N = finalizedSessions.size();
-            for (int i = 0; i < N; i++) {
-                final PackageInstallerSession session = finalizedSessions.get(i);
-                session.dump(pw);
-                pw.println();
-            }
-            pw.println();
-            pw.decreaseIndent();
+            finalizedSessionMap.dump("Finalized", pw);
 
             pw.println("Historical install sessions:");
             pw.increaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e71ac1a..aa005a3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -146,13 +146,7 @@
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedMainComponent;
 import android.content.pm.parsing.component.ParsedProvider;
-
-import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.PackageStateUtils;
-import com.android.server.pm.pkg.PackageUserState;
-import com.android.server.pm.pkg.PackageUserStateInternal;
 import android.content.pm.pkg.PackageUserStateUtils;
-import com.android.server.pm.pkg.SuspendParams;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -257,6 +251,11 @@
 import com.android.server.pm.pkg.AndroidPackageApi;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateImpl;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -1829,8 +1828,7 @@
         mBroadcastHelper = new BroadcastHelper(mInjector);
         mAppDataHelper = new AppDataHelper(this);
         mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
-        mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this, mRemovePackageHelper,
-                mAppDataHelper);
+        mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
         mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
                 mAppDataHelper);
         mPreferredActivityHelper = new PreferredActivityHelper(this);
@@ -2073,7 +2071,7 @@
                 int size = packageSettings.size();
                 for (int i = 0; i < size; i++) {
                     final PackageSetting ps = packageSettings.valueAt(i);
-                    if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
                         continue;
                     }
                     ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
@@ -2909,7 +2907,6 @@
                     volumeUuid);
             final boolean aggressive = (storageFlags
                     & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
-            final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags);
 
             // 1. Pre-flight to determine if we have any chance to succeed
             // 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
@@ -2926,10 +2923,11 @@
             }
 
             // 4. Consider cached app data (above quotas)
-            try {
-                mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
-                        Installer.FLAG_FREE_CACHE_V2);
-            } catch (InstallerException ignored) {
+            synchronized (mInstallLock) {
+                try {
+                    mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2);
+                } catch (InstallerException ignored) {
+                }
             }
             if (file.getUsableSpace() >= bytes) return;
 
@@ -2953,10 +2951,12 @@
             }
 
             // 8. Consider cached app data (below quotas)
-            try {
-                mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
-                        Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
-            } catch (InstallerException ignored) {
+            synchronized (mInstallLock) {
+                try {
+                    mInstaller.freeCache(volumeUuid, bytes,
+                            Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+                } catch (InstallerException ignored) {
+                }
             }
             if (file.getUsableSpace() >= bytes) return;
 
@@ -2982,9 +2982,11 @@
             // 12. Clear temp install session files
             mInstallerService.freeStageDirs(volumeUuid);
         } else {
-            try {
-                mInstaller.freeCache(volumeUuid, bytes, 0, 0);
-            } catch (InstallerException ignored) {
+            synchronized (mInstallLock) {
+                try {
+                    mInstaller.freeCache(volumeUuid, bytes, 0);
+                } catch (InstallerException ignored) {
+                }
             }
         }
         if (file.getUsableSpace() >= bytes) return;
@@ -3368,11 +3370,11 @@
 
             final String libName = libInfo.getName();
             if (libInfo.isStatic()) {
-                final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
+                final int libIdx = ArrayUtils.indexOf(ps.getUsesStaticLibraries(), libName);
                 if (libIdx < 0) {
                     continue;
                 }
-                if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) {
+                if (ps.getUsesStaticLibrariesVersions()[libIdx] != libInfo.getLongVersion()) {
                     continue;
                 }
                 if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
@@ -4044,13 +4046,13 @@
                 if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
                     return 0;
                 }
-                return sus.pkgFlags;
+                return sus.getFlags();
             } else if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
                 if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                     return 0;
                 }
-                return ps.pkgFlags;
+                return ps.getFlags();
             }
         }
         return 0;
@@ -4071,13 +4073,13 @@
                 if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
                     return 0;
                 }
-                return sus.pkgPrivateFlags;
+                return sus.getPrivateFlags();
             } else if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
                 if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                     return 0;
                 }
-                return ps.pkgPrivateFlags;
+                return ps.getPrivateFlags();
             }
         }
         return 0;
@@ -6865,9 +6867,11 @@
                 return false;
             }
             if (systemUserApp) {
-                ps.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+                ps.setPrivateFlags(ps.getPrivateFlags()
+                        | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
             } else {
-                ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+                ps.setPrivateFlags(ps.getPrivateFlags()
+                        & ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
             }
             writeSettingsLPrTEMP();
         }
@@ -7798,7 +7802,7 @@
                             : "Unknown package: " + packageName);
                 }
                 if (callingUid == Process.SHELL_UID
-                        && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
+                        && (pkgSetting.getFlags() & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
                     // Shell can only change whole packages between ENABLED and DISABLED_USER states
                     // unless it is a test package.
                     final int oldState = pkgSetting.getEnabled(userId);
@@ -11186,4 +11190,28 @@
         }
         return scanFlags;
     }
+
+    Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile,
+            int systemScanFlags, int systemParseFlags) {
+        List<ScanPartition> dirsToScanAsSystem =
+                mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+        @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
+        @PackageManagerService.ScanFlags int rescanFlags = 0;
+        for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+            final ScanPartition partition = dirsToScanAsSystem.get(i1);
+            if (partition.containsPrivApp(scanFile)) {
+                reparseFlags = systemParseFlags;
+                rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+                        | partition.scanFlag;
+                break;
+            }
+            if (partition.containsApp(scanFile)) {
+                reparseFlags = systemParseFlags;
+                rescanFlags = systemScanFlags | partition.scanFlag;
+                break;
+            }
+        }
+        return new Pair<>(rescanFlags, reparseFlags);
+    }
+
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 098c42e..d13a554 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -989,11 +989,11 @@
     }
 
     public static boolean isSystemApp(PackageSetting ps) {
-        return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
+        return (ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
     public static boolean isUpdatedSystemApp(PackageSetting ps) {
-        return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+        return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
     // Static to give access to ComputeEngine
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3554cc0..fc59541 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -81,6 +81,7 @@
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.incremental.V4Signature;
@@ -2629,6 +2630,7 @@
         if (userType == null) {
             userType = UserInfo.getDefaultUserType(flags);
         }
+        Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "shell_runCreateUser");
         try {
             if (UserManager.isUserTypeRestricted(userType)) {
                 // In non-split user mode, userId can only be SYSTEM
@@ -2645,6 +2647,8 @@
             }
         } catch (ServiceSpecificException e) {
             getErrPrintWriter().println("Error: " + e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
         }
 
         if (info != null) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 64fb630..de9c732 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -84,31 +84,20 @@
      * shared user setting with the package setting. The shared user ID lets us link the
      * two objects.
      */
-    protected int sharedUserId;
+    private int sharedUserId;
 
-    /**
-     * @see PackageState#getMimeGroups()
-     */
     @Nullable
-    Map<String, ArraySet<String>> mimeGroups;
+    private Map<String, Set<String>> mimeGroups;
 
-    /**
-     * Non-persisted value. During an "upgrade without restart", we need the set
-     * of all previous code paths so we can surgically add the new APKs to the
-     * active classloader. If at any point an application is upgraded with a
-     * restart, this field will be cleared since the classloader would be created
-     * using the full set of code paths when the package's process is started.
-     * TODO: Remove
-     */
     @Deprecated
     @Nullable
-    Set<String> mOldCodePaths;
+    private Set<String> mOldCodePaths;
 
     @Nullable
-    String[] usesStaticLibraries;
+    private String[] usesStaticLibraries;
 
     @Nullable
-    long[] usesStaticLibrariesVersions;
+    private long[] usesStaticLibrariesVersions;
 
     /**
      * The path under which native libraries have been unpacked. This path is
@@ -220,7 +209,7 @@
             String secondaryCpuAbi, String cpuAbiOverride,
             long longVersionCode, int pkgFlags, int pkgPrivateFlags,
             int sharedUserId, String[] usesStaticLibraries,
-            long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups,
+            long[] usesStaticLibrariesVersions, Map<String, Set<String>> mimeGroups,
             @NonNull UUID domainSetId) {
         super(pkgFlags, pkgPrivateFlags);
         this.mName = name;
@@ -322,7 +311,7 @@
     }
 
     public List<String> getMimeGroup(String mimeGroup) {
-        ArraySet<String> mimeTypes = getMimeGroupInternal(mimeGroup);
+        Set<String> mimeTypes = getMimeGroupInternal(mimeGroup);
         if (mimeTypes == null) {
             throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
                     + " for package " + mName);
@@ -330,7 +319,7 @@
         return new ArrayList<>(mimeTypes);
     }
 
-    private ArraySet<String> getMimeGroupInternal(String mimeGroup) {
+    private Set<String> getMimeGroupInternal(String mimeGroup) {
         return mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
     }
 
@@ -411,7 +400,7 @@
     }
 
     public boolean setMimeGroup(String mimeGroup, List<String> mimeTypes) {
-        ArraySet<String> oldMimeTypes = getMimeGroupInternal(mimeGroup);
+        Set<String> oldMimeTypes = getMimeGroupInternal(mimeGroup);
         if (oldMimeTypes == null) {
             throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
                     + " for package " + mName);
@@ -458,7 +447,7 @@
 
     @Override
     public boolean isExternalStorage() {
-        return (pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+        return (getFlags() & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
     public PackageSetting setUpdateAvailable(boolean updateAvailable) {
@@ -481,7 +470,7 @@
                 + " " + mName + "/" + mAppId + "}";
     }
 
-    protected void copyMimeGroups(@Nullable Map<String, ArraySet<String>> newMimeGroups) {
+    protected void copyMimeGroups(@Nullable Map<String, Set<String>> newMimeGroups) {
         if (newMimeGroups == null) {
             mimeGroups = null;
             return;
@@ -489,7 +478,7 @@
 
         mimeGroups = new ArrayMap<>(newMimeGroups.size());
         for (String mimeGroup : newMimeGroups.keySet()) {
-            ArraySet<String> mimeTypes = newMimeGroups.get(mimeGroup);
+            Set<String> mimeTypes = newMimeGroups.get(mimeGroup);
 
             if (mimeTypes != null) {
                 mimeGroups.put(mimeGroup, new ArraySet<>(mimeTypes));
@@ -524,7 +513,7 @@
             mimeGroups = Collections.emptyMap();
         }
 
-        ArrayMap<String, ArraySet<String>> updatedMimeGroups =
+        ArrayMap<String, Set<String>> updatedMimeGroups =
                 new ArrayMap<>(newMimeGroupNames.size());
 
         for (String mimeGroup : newMimeGroupNames) {
@@ -553,36 +542,36 @@
     }
 
     public boolean isPrivileged() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
 
     public boolean isOem() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
     }
 
     public boolean isVendor() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
     public boolean isProduct() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
     }
 
     @Override
     public boolean isRequiredForSystemUser() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
     }
 
     public boolean isSystemExt() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
     }
 
     public boolean isOdm() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+        return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
     }
 
     public boolean isSystem() {
-        return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
+        return (getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
     public SigningDetails getSigningDetails() {
@@ -1210,6 +1199,9 @@
         return versionCode;
     }
 
+    /**
+     * @see PackageState#getMimeGroups()
+     */
     @Nullable
     @Override
     public Map<String, Set<String>> getMimeGroups() {
@@ -1305,6 +1297,30 @@
         return this;
     }
 
+    public PackageSetting setMimeGroups(@NonNull Map<String, Set<String>> mimeGroups) {
+        this.mimeGroups = mimeGroups;
+        onChanged();
+        return this;
+    }
+
+    public PackageSetting setOldCodePaths(Set<String> oldCodePaths) {
+        mOldCodePaths = oldCodePaths;
+        onChanged();
+        return this;
+    }
+
+    public PackageSetting setUsesStaticLibraries(String[] usesStaticLibraries) {
+        this.usesStaticLibraries = usesStaticLibraries;
+        onChanged();
+        return this;
+    }
+
+    public PackageSetting setUsesStaticLibrariesVersions(long[] usesStaticLibrariesVersions) {
+        this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
+        onChanged();
+        return this;
+    }
+
     @NonNull
     @Override
     public PackageStateUnserialized getTransientState() {
@@ -1316,14 +1332,18 @@
         return mUserStates;
     }
 
-    @Override
-    public int getPkgFlags() {
-        return pkgFlags;
-    }
+    public PackageSetting addMimeTypes(String mimeGroup, Set<String> mimeTypes) {
+        if (mimeGroups == null) {
+            mimeGroups = new ArrayMap<>();
+        }
 
-    @Override
-    public int getPkgPrivateFlags() {
-        return pkgPrivateFlags;
+        Set<String> existingMimeTypes = mimeGroups.get(mimeGroup);
+        if (existingMimeTypes == null) {
+            existingMimeTypes = new ArraySet<>();
+            mimeGroups.put(mimeGroup, existingMimeTypes);
+        }
+        existingMimeTypes.addAll(mimeTypes);
+        return this;
     }
 
 
@@ -1341,14 +1361,6 @@
     //@formatter:off
 
 
-    /**
-     * Non-persisted value. During an "upgrade without restart", we need the set
-     * of all previous code paths so we can surgically add the new APKs to the
-     * active classloader. If at any point an application is upgraded with a
-     * restart, this field will be cleared since the classloader would be created
-     * using the full set of code paths when the package's process is started.
-     * TODO: Remove
-     */
     @DataClass.Generated.Member
     public @Deprecated @Nullable Set<String> getOldCodePaths() {
         return mOldCodePaths;
@@ -1504,10 +1516,10 @@
     }
 
     @DataClass.Generated(
-            time = 1635392392447L,
+            time = 1636652483231L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
-            inputSignatures = "protected  int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long firstInstallTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate  boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate  boolean updateAvailable\nprivate  boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate  android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic  boolean isSharedUser()\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  java.lang.String getLastDisabledAppCaller(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n  boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n  boolean isAnyInstalled(int[])\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\n  boolean getSuspended(int)\n  boolean isSuspendedBy(java.lang.String,int)\n  boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n  boolean removeSuspension(java.lang.String,int)\n  void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n  android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n  void setHarmfulAppWarning(int,java.lang.String)\n  java.lang.String getHarmfulAppWarning(int)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\n  java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic  void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isPackageLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic @java.lang.Override int getPkgFlags()\npublic @java.lang.Override int getPkgPrivateFlags()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+            inputSignatures = "private  int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate  int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate  float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate  long mLastModifiedTime\nprivate  long firstInstallTime\nprivate  long lastUpdateTime\nprivate  long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate  boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate  int categoryOverride\nprivate  boolean updateAvailable\nprivate  boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate  com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic  com.android.server.pm.PackageSetting snapshot()\npublic  void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic  java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate  java.util.Set<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic  boolean isSharedUser()\npublic  com.android.server.pm.PackageSetting setAppId(int)\npublic  com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic  com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic  com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic  com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic  com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n  com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic  com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic  com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic  com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic  com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic  com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic  boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic  com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic  com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic  com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic  com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic  int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected  void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  void updateFrom(com.android.server.pm.PackageSetting)\n  com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic  com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic  boolean isPrivileged()\npublic  boolean isOem()\npublic  boolean isVendor()\npublic  boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic  boolean isSystemExt()\npublic  boolean isOdm()\npublic  boolean isSystem()\npublic  android.content.pm.SigningDetails getSigningDetails()\npublic  com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic  void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n  void setEnabled(int,int,java.lang.String)\n  int getEnabled(int)\n  java.lang.String getLastDisabledAppCaller(int)\n  void setInstalled(boolean,int)\n  boolean getInstalled(int)\n  int getInstallReason(int)\n  void setInstallReason(int,int)\n  int getUninstallReason(int)\n  void setUninstallReason(int,int)\n  boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n  boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n  boolean isAnyInstalled(int[])\n  int[] queryInstalledUsers(int[],boolean)\n  long getCeDataInode(int)\n  void setCeDataInode(long,int)\n  boolean getStopped(int)\n  void setStopped(boolean,int)\n  boolean getNotLaunched(int)\n  void setNotLaunched(boolean,int)\n  boolean getHidden(int)\n  void setHidden(boolean,int)\n  int getDistractionFlags(int)\n  void setDistractionFlags(int,int)\n  boolean getSuspended(int)\n  boolean isSuspendedBy(java.lang.String,int)\n  boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n  boolean removeSuspension(java.lang.String,int)\n  void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic  boolean getInstantApp(int)\n  void setInstantApp(boolean,int)\n  boolean getVirtualPreload(int)\n  void setVirtualPreload(boolean,int)\n  void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n  void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n  android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n  android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n  void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n  void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n  com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n  void addDisabledComponent(java.lang.String,int)\n  void addEnabledComponent(java.lang.String,int)\n  boolean enableComponentLPw(java.lang.String,int)\n  boolean disableComponentLPw(java.lang.String,int)\n  boolean restoreComponentLPw(java.lang.String,int)\n  int getCurrentEnabledStateLPr(java.lang.String,int)\n  void removeUser(int)\npublic  int[] getNotInstalledUserIds()\n  void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected  void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n  void setHarmfulAppWarning(int,java.lang.String)\n  java.lang.String getHarmfulAppWarning(int)\n  com.android.server.pm.PackageSetting setPath(java.io.File)\n  java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic  void resetOverrideComponentLabelIcon(int)\npublic  void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic  boolean isPackageLoading()\npublic  com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic  com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic  com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic  com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic  com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic  com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic  com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic  com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic  com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic  com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 731c6ca..6cc94ce 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -23,7 +23,6 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.os.incremental.IncrementalManager.isIncrementalPath;
 
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
@@ -92,7 +91,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -106,7 +104,6 @@
 import java.security.DigestException;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -118,66 +115,17 @@
  */
 final class ScanPackageHelper {
     final PackageManagerService mPm;
-    final InstallPackageHelper mInstallPackageHelper;
     final PackageManagerServiceInjector mInjector;
 
     // TODO(b/198166813): remove PMS dependency
     public ScanPackageHelper(PackageManagerService pm) {
         mPm = pm;
         mInjector = pm.mInjector;
-        mInstallPackageHelper = new InstallPackageHelper(mPm, mInjector);
     }
 
     ScanPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
         mPm = pm;
         mInjector = injector;
-        mInstallPackageHelper = new InstallPackageHelper(mPm, injector);
-    }
-
-    /**
-     * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
-     * the app ID required for reconcile.
-     * @return {@code true} if a new app ID was registered and will need to be cleaned up on
-     *         failure.
-     */
-    public boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
-            throws PackageManagerException {
-        if (!result.mExistingSettingCopied || result.needsNewAppId()) {
-            synchronized (mPm.mLock) {
-                // THROWS: when we can't allocate a user id. add call to check if there's
-                // enough space to ensure we won't throw; otherwise, don't modify state
-                return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.needsNewAppId());
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Reverts any app ID creation that were made by
-     * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
-     * referenced method returned true.
-     */
-    public void cleanUpAppIdCreation(@NonNull ScanResult result) {
-        // iff we've acquired an app ID for a new package setting, remove it so that it can be
-        // acquired by another request.
-        if (result.mPkgSetting.getAppId() > 0) {
-            mPm.mSettings.removeAppIdLPw(result.mPkgSetting.getAppId());
-        }
-    }
-
-    /**
-     *  Traces a package scan.
-     *  @see #scanPackageLI(File, int, int, long, UserHandle)
-     */
-    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    public AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
-            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
-        try {
-            return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
     }
 
     /**
@@ -197,31 +145,6 @@
         }
     }
 
-    /**
-     *  Scans a package and returns the newly parsed package.
-     *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
-     */
-    @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
-    private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
-            long currentTime, UserHandle user) throws PackageManagerException {
-        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
-
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
-        final ParsedPackage parsedPackage;
-        try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
-            parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-
-        // Static shared libraries have synthetic package names
-        if (parsedPackage.isStaticSharedLibrary()) {
-            PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
-        }
-
-        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
-    }
-
     // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
     // But, first, committing the results / removing app data needs to be moved up a level to the
     // callers of this method. Also, we need to solve the problem of potentially creating a new
@@ -611,10 +534,10 @@
         }
         pkgSetting.setLastModifiedTime(scanFileTime);
         // TODO(b/135203078): Remove, move to constructor
-        pkgSetting.setPkg(parsedPackage);
-        pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
-        pkgSetting.pkgPrivateFlags =
-                PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
+        pkgSetting.setPkg(parsedPackage)
+                .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
+                .setPrivateFlags(
+                        PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
         if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
             pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
         }
@@ -680,27 +603,27 @@
         if (systemPkgSetting != null)  {
             // updated system application, must at least have SCAN_AS_SYSTEM
             scanFlags |= SCAN_AS_SYSTEM;
-            if ((systemPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.getPrivateFlags()
                     & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
                 scanFlags |= SCAN_AS_PRIVILEGED;
             }
-            if ((systemPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.getPrivateFlags()
                     & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
                 scanFlags |= SCAN_AS_OEM;
             }
-            if ((systemPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.getPrivateFlags()
                     & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
                 scanFlags |= SCAN_AS_VENDOR;
             }
-            if ((systemPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.getPrivateFlags()
                     & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
                 scanFlags |= SCAN_AS_PRODUCT;
             }
-            if ((systemPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.getPrivateFlags()
                     & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
                 scanFlags |= SCAN_AS_SYSTEM_EXT;
             }
-            if ((systemPkgSetting.pkgPrivateFlags
+            if ((systemPkgSetting.getPrivateFlags()
                     & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
                 scanFlags |= SCAN_AS_ODM;
             }
@@ -748,22 +671,7 @@
         return scanFlags;
     }
 
-
-    /**
-     * Adds a new package to the internal data structures during platform initialization.
-     * <p>After adding, the package is known to the system and available for querying.
-     * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
-     * etc...], additional checks are performed. Basic verification [such as ensuring
-     * matching signatures, checking version codes, etc...] occurs if the package is
-     * identical to a previously known package. If the package fails a signature check,
-     * the version installed on /data will be removed. If the version of the new package
-     * is less than or equal than the version on /data, it will be ignored.
-     * <p>Regardless of the package location, the results are applied to the internal
-     * structures and the package is made available to the rest of the system.
-     * <p>NOTE: The return value should be removed. It's the passed in package object.
-     */
-    @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
-    public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
+    public Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
             @ParsingPackageUtils.ParseFlags int parseFlags,
             @PackageManagerService.ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
@@ -828,7 +736,7 @@
                     // we're updating the disabled package, so, scan it as the package setting
                     boolean isPlatformPackage = platformPackage != null
                             && platformPackage.getPackageName().equals(
-                                    parsedPackage.getPackageName());
+                            parsedPackage.getPackageName());
                     final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
                             null, disabledPkgSetting /* pkgSetting */,
                             null /* disabledPkgSetting */, null /* originalPkgSetting */,
@@ -872,7 +780,7 @@
 
             final InstallArgs args = new FileInstallArgs(
                     pkgSetting.getPathString(), getAppDexInstructionSets(
-                            pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
+                    pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
             args.cleanUpResourcesLI();
             synchronized (mPm.mLock) {
                 mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
@@ -957,7 +865,7 @@
                                 + parsedPackage.getPath());
                 InstallArgs args = new FileInstallArgs(
                         pkgSetting.getPathString(), getAppDexInstructionSets(
-                                pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
+                        pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
                         mPm);
                 synchronized (mPm.mInstallLock) {
                     args.cleanUpResourcesLI();
@@ -977,52 +885,9 @@
             }
         }
 
-        final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
-                | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
-        if (scanResult.mSuccess) {
-            synchronized (mPm.mLock) {
-                boolean appIdCreated = false;
-                try {
-                    final String pkgName = scanResult.mPkgSetting.getPackageName();
-                    final Map<String, ReconciledPackage> reconcileResult =
-                            mInstallPackageHelper.reconcilePackagesLocked(
-                                    new ReconcileRequest(
-                                            Collections.singletonMap(pkgName, scanResult),
-                                            mPm.mSharedLibraries,
-                                            mPm.mPackages,
-                                            Collections.singletonMap(
-                                                    pkgName,
-                                                    mPm.getSettingsVersionForPackage(
-                                                            parsedPackage)),
-                                            Collections.singletonMap(pkgName,
-                                                    mPm.getSharedLibLatestVersionSetting(
-                                                            scanResult))),
-                                    mPm.mSettings.getKeySetManagerService(), mInjector);
-                    appIdCreated = optimisticallyRegisterAppId(scanResult);
-                    mInstallPackageHelper.commitReconciledScanResultLocked(
-                            reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
-                } catch (PackageManagerException e) {
-                    if (appIdCreated) {
-                        cleanUpAppIdCreation(scanResult);
-                    }
-                    throw e;
-                }
-            }
-        }
-
-        if (shouldHideSystemApp) {
-            synchronized (mPm.mLock) {
-                mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
-            }
-        }
-        if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
-            if (pkgSetting != null && pkgSetting.isPackageLoading()) {
-                // Continue monitoring loading progress of active incremental packages
-                mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
-                        new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
-            }
-        }
-        return scanResult.mPkgSetting.getPkg();
+        final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
+                scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+        return new Pair<>(scanResult, shouldHideSystemApp);
     }
 
     /**
@@ -1085,7 +950,7 @@
                 && ps.getLastModifiedTime() == lastModifiedTime
                 && !InstallPackageHelper.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
                 && !InstallPackageHelper.isRecoverSignatureUpdateNeeded(
-                        settingsVersionForPackage)) {
+                settingsVersionForPackage)) {
             if (ps.getSigningDetails().getSignatures() != null
                     && ps.getSigningDetails().getSignatures().length != 0
                     && ps.getSigningDetails().getSignatureSchemeVersion()
@@ -1191,7 +1056,7 @@
 
     @GuardedBy("mPm.mLock")
     private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
-        if ((oldPkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+        if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
             Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
                     + " to " + newPkg.getPackageName()
                     + ": old package not in system partition");
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 22e6ece..ed85ff9 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -29,15 +29,11 @@
 
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public abstract class SettingBase implements Watchable, Snappable {
-    // TODO: make this variable protected, or even private with a getter and setter.
-    // Simply making it protected or private requires that the name be changed to conformm
-    // to the Android naming convention, and that touches quite a few files.
-    int pkgFlags;
 
-    // TODO: make this variable protected, or even private with a getter and setter.
-    // Simply making it protected or private requires that the name be changed to conformm
-    // to the Android naming convention, and that touches quite a few files.
-    int pkgPrivateFlags;
+    // TODO: Remove in favor of individual boolean APIs. It's not clear what flag values are saved
+    //  and bugs exist where callers query for an unsaved flag.
+    private int mPkgFlags;
+    private int mPkgPrivateFlags;
 
     /**
      * Watchable machinery
@@ -115,8 +111,8 @@
     }
 
     public final void copySettingBase(SettingBase orig) {
-        pkgFlags = orig.pkgFlags;
-        pkgPrivateFlags = orig.pkgPrivateFlags;
+        mPkgFlags = orig.mPkgFlags;
+        mPkgPrivateFlags = orig.mPkgPrivateFlags;
         mLegacyPermissionsState.copyFrom(orig.mLegacyPermissionsState);
         onChanged();
     }
@@ -126,15 +122,17 @@
         return mLegacyPermissionsState;
     }
 
-    void setFlags(int pkgFlags) {
-        this.pkgFlags = pkgFlags
+    SettingBase setFlags(int pkgFlags) {
+        this.mPkgFlags = pkgFlags
                 & (ApplicationInfo.FLAG_SYSTEM
-                        | ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+                        | ApplicationInfo.FLAG_EXTERNAL_STORAGE
+                        | ApplicationInfo.FLAG_TEST_ONLY);
         onChanged();
+        return this;
     }
 
-    void setPrivateFlags(int pkgPrivateFlags) {
-        this.pkgPrivateFlags = pkgPrivateFlags
+    SettingBase setPrivateFlags(int pkgPrivateFlags) {
+        this.mPkgPrivateFlags = pkgPrivateFlags
                 & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
                 | ApplicationInfo.PRIVATE_FLAG_OEM
                 | ApplicationInfo.PRIVATE_FLAG_VENDOR
@@ -143,5 +141,14 @@
                 | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
                 | ApplicationInfo.PRIVATE_FLAG_ODM);
         onChanged();
+        return this;
+    }
+
+    public int getFlags() {
+        return mPkgFlags;
+    }
+
+    public int getPrivateFlags() {
+        return mPkgPrivateFlags;
     }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3d6467e..7eec791 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -154,6 +154,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -825,8 +826,8 @@
         PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(),
                 p.getLegacyNativeLibraryPath(), p.getPrimaryCpuAbi(),
                 p.getSecondaryCpuAbi(), p.getCpuAbiOverride(),
-                p.getAppId(), p.getVersionCode(), p.pkgFlags, p.pkgPrivateFlags,
-                p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups,
+                p.getAppId(), p.getVersionCode(), p.getFlags(), p.getPrivateFlags(),
+                p.getUsesStaticLibraries(), p.getUsesStaticLibrariesVersions(), p.getMimeGroups(),
                 mDomainVerificationManager.generateNewId());
         if (ret != null) {
             ret.getPkgState().setUpdatedSystemApp(false);
@@ -851,7 +852,7 @@
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
             pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
-            long[] usesStaticLibraryNames, Map<String, ArraySet<String>> mimeGroups,
+            long[] usesStaticLibraryNames, Map<String, Set<String>> mimeGroups,
             @NonNull UUID domainSetId) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
@@ -931,22 +932,22 @@
         if (originalPkg != null) {
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
                     + pkgName + " is adopting original package " + originalPkg.getPackageName());
-            pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
-            pkgSetting.setPath(codePath);
-            pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
-            pkgSetting.pkgFlags = pkgFlags;
-            pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
-            pkgSetting.setPrimaryCpuAbi(primaryCpuAbi);
-            pkgSetting.setSecondaryCpuAbi(secondaryCpuAbi);
-            // NOTE: Create a deeper copy of the package signatures so we don't
-            // overwrite the signatures in the original package setting.
-            pkgSetting.setSignatures(new PackageSignatures());
-            pkgSetting.setLongVersionCode(versionCode);
-            pkgSetting.usesStaticLibraries = usesStaticLibraries;
-            pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
-            // Update new package state.
-            pkgSetting.setLastModifiedTime(codePath.lastModified());
-            pkgSetting.setDomainSetId(domainSetId);
+            pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/)
+                    .setPath(codePath)
+                    .setLegacyNativeLibraryPath(legacyNativeLibraryPath)
+                    .setPrimaryCpuAbi(primaryCpuAbi)
+                    .setSecondaryCpuAbi(secondaryCpuAbi)
+                    // NOTE: Create a deeper copy of the package signatures so we don't
+                    // overwrite the signatures in the original package setting.
+                    .setSignatures(new PackageSignatures())
+                    .setLongVersionCode(versionCode)
+                    .setUsesStaticLibraries(usesStaticLibraries)
+                    .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
+                    // Update new package state.
+                    .setLastModifiedTime(codePath.lastModified())
+                    .setDomainSetId(domainSetId);
+            pkgSetting.setFlags(pkgFlags)
+                    .setPrivateFlags(pkgPrivateFlags);
         } else {
             pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
                     legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
@@ -1028,7 +1029,7 @@
         return pkgSetting;
     }
 
-    private static Map<String, ArraySet<String>> createMimeGroups(Set<String> mimeGroupNames) {
+    private static Map<String, Set<String>> createMimeGroups(Set<String> mimeGroupNames) {
         if (mimeGroupNames == null) {
             return null;
         }
@@ -1090,42 +1091,40 @@
             }
             pkgSetting.setPath(codePath);
         }
-        // If what we are scanning is a system (and possibly privileged) package,
-        // then make it so, regardless of whether it was previously installed only
-        // in the data partition. Reset first.
-        pkgSetting.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
-        pkgSetting.pkgPrivateFlags &= ~(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
-                | ApplicationInfo.PRIVATE_FLAG_OEM
-                | ApplicationInfo.PRIVATE_FLAG_VENDOR
-                | ApplicationInfo.PRIVATE_FLAG_PRODUCT
-                | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT
-                | ApplicationInfo.PRIVATE_FLAG_ODM);
-        pkgSetting.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
-        pkgSetting.pkgPrivateFlags |=
-                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-        pkgSetting.pkgPrivateFlags |=
-                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM;
-        pkgSetting.pkgPrivateFlags |=
-                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR;
-        pkgSetting.pkgPrivateFlags |=
-                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT;
-        pkgSetting.pkgPrivateFlags |=
-                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT;
-        pkgSetting.pkgPrivateFlags |=
-                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM;
-        pkgSetting.setPrimaryCpuAbi(primaryCpuAbi);
-        pkgSetting.setSecondaryCpuAbi(secondaryCpuAbi);
+
+        pkgSetting.setPrimaryCpuAbi(primaryCpuAbi)
+                .setSecondaryCpuAbi(secondaryCpuAbi)
+                .updateMimeGroups(mimeGroupNames)
+                .setDomainSetId(domainSetId);
         // Update static shared library dependencies if needed
         if (usesStaticLibraries != null && usesStaticLibrariesVersions != null
                 && usesStaticLibraries.length == usesStaticLibrariesVersions.length) {
-            pkgSetting.usesStaticLibraries = usesStaticLibraries;
-            pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
+            pkgSetting.setUsesStaticLibraries(usesStaticLibraries)
+                    .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions);
         } else {
-            pkgSetting.usesStaticLibraries = null;
-            pkgSetting.usesStaticLibrariesVersions = null;
+            pkgSetting.setUsesStaticLibraries(null)
+                    .setUsesStaticLibrariesVersions(null);
         }
-        pkgSetting.updateMimeGroups(mimeGroupNames);
-        pkgSetting.setDomainSetId(domainSetId);
+
+        // These two flags are preserved from the existing PackageSetting. Copied from prior code,
+        // unclear if this is actually necessary.
+        boolean wasExternalStorage = (pkgSetting.getFlags()
+                & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+        if (wasExternalStorage) {
+            pkgFlags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+        } else {
+            pkgFlags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+        }
+        boolean wasRequiredForSystemUser = (pkgSetting.getPrivateFlags()
+                & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+        if (wasRequiredForSystemUser) {
+            pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+        } else {
+            pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+        }
+
+        pkgSetting.setFlags(pkgFlags)
+                .setPrivateFlags(pkgPrivateFlags);
     }
 
     /**
@@ -2175,10 +2174,10 @@
         long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1);
 
         if (libName != null && libVersion >= 0) {
-            outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class,
-                    outPs.usesStaticLibraries, libName);
-            outPs.usesStaticLibrariesVersions = ArrayUtils.appendLong(
-                    outPs.usesStaticLibrariesVersions, libVersion);
+            outPs.setUsesStaticLibraries(ArrayUtils.appendElement(String.class,
+                    outPs.getUsesStaticLibraries(), libName));
+            outPs.setUsesStaticLibrariesVersions(ArrayUtils.appendLong(
+                    outPs.getUsesStaticLibrariesVersions(), libVersion));
         }
 
         XmlUtils.skipCurrentTag(parser);
@@ -2709,7 +2708,8 @@
         }
         serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
 
-        writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+        writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
+                pkg.getUsesStaticLibrariesVersions());
 
         serializer.endTag(null, "updated-package");
     }
@@ -2736,8 +2736,8 @@
             serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride());
         }
 
-        serializer.attributeInt(null, "publicFlags", pkg.pkgFlags);
-        serializer.attributeInt(null, "privateFlags", pkg.pkgPrivateFlags);
+        serializer.attributeInt(null, "publicFlags", pkg.getFlags());
+        serializer.attributeInt(null, "privateFlags", pkg.getPrivateFlags());
         serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
         serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
         serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
@@ -2786,7 +2786,8 @@
 
         serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
 
-        writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+        writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
+                pkg.getUsesStaticLibrariesVersions());
 
         pkg.getSignatures().writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
 
@@ -2798,7 +2799,7 @@
         writeSigningKeySetLPr(serializer, pkg.getKeySetData());
         writeUpgradeKeySetsLPr(serializer, pkg.getKeySetData());
         writeKeySetAliasesLPr(serializer, pkg.getKeySetData());
-        writeMimeGroupLPr(serializer, pkg.mimeGroups);
+        writeMimeGroupLPr(serializer, pkg.getMimeGroups());
 
         serializer.endTag(null, "package");
     }
@@ -3071,7 +3072,7 @@
         final PackageManagerInternal pmInternal =
                 LocalServices.getService(PackageManagerInternal.class);
         for (PackageSetting ps : mPackages.values()) {
-            if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.getPkg() != null
+            if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.getPkg() != null
                     && !ps.getPkg().getPreferredActivityFilters().isEmpty()) {
                 List<Pair<String, ParsedIntentInfo>> intents
                         = ps.getPkg().getPreferredActivityFilters();
@@ -3786,7 +3787,11 @@
                         Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
                     }
                 } else if (tagName.equals(TAG_MIME_GROUP)) {
-                    packageSetting.mimeGroups = readMimeGroupLPw(parser, packageSetting.mimeGroups);
+                    final Pair<String, Set<String>> groupToMimeTypes = readMimeGroupLPw(parser);
+                    if (groupToMimeTypes != null) {
+                        packageSetting.addMimeTypes(groupToMimeTypes.first,
+                                groupToMimeTypes.second);
+                    }
                 } else if (tagName.equals(TAG_USES_STATIC_LIB)) {
                     readUsesStaticLibLPw(parser, packageSetting);
                 } else {
@@ -3812,23 +3817,16 @@
         }
     }
 
-    private Map<String, ArraySet<String>> readMimeGroupLPw(TypedXmlPullParser parser,
-            Map<String, ArraySet<String>> mimeGroups) throws XmlPullParserException, IOException {
+    @Nullable
+    private Pair<String, Set<String>> readMimeGroupLPw(TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
         String groupName = parser.getAttributeValue(null, ATTR_NAME);
         if (groupName == null) {
             XmlUtils.skipCurrentTag(parser);
-            return mimeGroups;
+            return null;
         }
 
-        if (mimeGroups == null) {
-            mimeGroups = new ArrayMap<>();
-        }
-
-        ArraySet<String> mimeTypes = mimeGroups.get(groupName);
-        if (mimeTypes == null) {
-            mimeTypes = new ArraySet<>();
-            mimeGroups.put(groupName, mimeTypes);
-        }
+        Set<String> mimeTypes = new ArraySet<>();
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3850,11 +3848,11 @@
             }
         }
 
-        return mimeGroups;
+        return Pair.create(groupName, mimeTypes);
     }
 
     private void writeMimeGroupLPr(TypedXmlSerializer serializer,
-            Map<String, ArraySet<String>> mimeGroups) throws IOException {
+            Map<String, Set<String>> mimeGroups) throws IOException {
         if (mimeGroups == null) {
             return;
         }
@@ -4477,7 +4475,7 @@
             pw.print(prefix); pw.print("  legacyNativeLibraryDir=");
             pw.println(ps.getLegacyNativeLibraryPath());
             pw.print(prefix); pw.print("  extractNativeLibs=");
-            pw.println((ps.pkgFlags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0
+            pw.println((ps.getFlags() & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0
                     ? "true" : "false");
             pw.print(prefix); pw.print("  primaryCpuAbi="); pw.println(ps.getPrimaryCpuAbi());
             pw.print(prefix); pw.print("  secondaryCpuAbi="); pw.println(ps.getSecondaryCpuAbi());
@@ -4687,7 +4685,7 @@
         pw.print(prefix); pw.print("  installPermissionsFixed=");
                 pw.print(ps.isInstallPermissionsFixed());
                 pw.println();
-        pw.print(prefix); pw.print("  pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
+        pw.print(prefix); pw.print("  pkgFlags="); printFlags(pw, ps.getFlags(), FLAG_DUMP_SPEC);
                 pw.println();
 
         if (pkg != null && pkg.getOverlayTarget() != null) {
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 3387737..7487e81 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -147,17 +147,17 @@
             return false;
         }
         // recalculate the pkgFlags for this shared user if needed
-        if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
+        if ((this.getFlags() & packageSetting.getFlags()) != 0) {
             int aggregatedFlags = uidFlags;
             for (PackageSetting ps : packages) {
-                aggregatedFlags |= ps.pkgFlags;
+                aggregatedFlags |= ps.getFlags();
             }
             setFlags(aggregatedFlags);
         }
-        if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
+        if ((this.getPrivateFlags() & packageSetting.getPrivateFlags()) != 0) {
             int aggregatedPrivateFlags = uidPrivateFlags;
             for (PackageSetting ps : packages) {
-                aggregatedPrivateFlags |= ps.pkgPrivateFlags;
+                aggregatedPrivateFlags |= ps.getPrivateFlags();
             }
             setPrivateFlags(aggregatedPrivateFlags);
         }
@@ -174,8 +174,8 @@
             seInfoTargetSdkVersion = packageSetting.getPkg().getTargetSdkVersion();
         }
         if (packages.add(packageSetting)) {
-            setFlags(this.pkgFlags | packageSetting.pkgFlags);
-            setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
+            setFlags(this.getFlags() | packageSetting.getFlags());
+            setPrivateFlags(this.getPrivateFlags() | packageSetting.getPrivateFlags());
             onChanged();
         }
         if (packageSetting.getPkg() != null) {
@@ -201,7 +201,7 @@
     }
 
     public boolean isPrivileged() {
-        return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+        return (this.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index b4bd086..42c88b3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -22,8 +22,6 @@
 import android.app.appsearch.AppSearchManager;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSession;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.GetByDocumentIdRequest;
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.PutDocumentsRequest;
 import android.app.appsearch.RemoveByDocumentIdRequest;
@@ -56,11 +54,12 @@
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.ShortcutService.DumpFilter;
@@ -88,7 +87,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -160,6 +159,8 @@
 
     private final Object mLock = new Object();
 
+    private final Executor mExecutor;
+
     /**
      * An temp in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
      */
@@ -189,11 +190,8 @@
 
     private long mLastKnownForegroundElapsedTime;
 
-    private boolean mIsInitilized;
-
-    private boolean mRescanRequired;
-    private boolean mIsNewApp;
-    private List<ShortcutInfo> mManifestShortcuts;
+    @GuardedBy("mLock")
+    private boolean mIsAppSearchSchemaUpToDate;
 
     private ShortcutPackage(ShortcutUser shortcutUser,
             int packageUserId, String packageName, ShortcutPackageInfo spi) {
@@ -201,6 +199,7 @@
                 spi != null ? spi : ShortcutPackageInfo.newEmpty());
 
         mPackageUid = shortcutUser.mService.injectGetPackageUid(packageName, packageUserId);
+        mExecutor = BackgroundThread.getExecutor();
     }
 
     public ShortcutPackage(ShortcutUser shortcutUser, int packageUserId, String packageName) {
@@ -245,11 +244,11 @@
         final String query = String.format("%s:-%s AND %s:%s",
                 AppSearchShortcutInfo.KEY_FLAGS, ShortcutInfo.FLAG_SHADOW,
                 AppSearchShortcutInfo.KEY_DISABLED_REASON, restoreBlockReason);
-        forEachShortcutMutateIf(query, si -> {
+        forEachShortcutMutate(si -> {
             if (restoreBlockReason == ShortcutInfo.DISABLED_REASON_NOT_DISABLED
                     && !si.hasFlags(ShortcutInfo.FLAG_SHADOW)
                     && si.getDisabledReason() == restoreBlockReason) {
-                return false;
+                return;
             }
             si.clearFlags(ShortcutInfo.FLAG_SHADOW);
 
@@ -257,7 +256,6 @@
             if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
                 si.addFlags(ShortcutInfo.FLAG_DISABLED);
             }
-            return true;
         });
         // Because some launchers may not have been restored (e.g. allowBackup=false),
         // we need to re-calculate the pinned shortcuts.
@@ -270,8 +268,7 @@
     @Nullable
     public ShortcutInfo findShortcutById(@Nullable final String id) {
         if (id == null) return null;
-        final List<ShortcutInfo> ret = getShortcutById(Collections.singleton(id));
-        return (ret == null || ret.isEmpty()) ? null : ret.get(0);
+        return mShortcuts.get(id);
     }
 
     public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
@@ -337,9 +334,8 @@
      * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
      */
     private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
-        final ShortcutInfo shortcut = findShortcutById(id);
+        final ShortcutInfo shortcut = mShortcuts.remove(id);
         if (shortcut != null) {
-            removeShortcut(id);
             mShortcutUser.mService.removeIconLocked(shortcut);
             shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
                     | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
@@ -435,7 +431,8 @@
                 }
 
                 changedShortcuts.add(shortcut);
-                deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null;
+                deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true,
+                        /*ignorePersistedShortcuts=*/ true) != null;
             }
         } else {
             // It's an update case.
@@ -449,16 +446,14 @@
 
         forceReplaceShortcutInner(newShortcut);
         if (isAppSearchEnabled()) {
-            mShortcutUser.mService.injectPostToHandler(() -> awaitInAppSearch("reportUsage",
-                    session -> {
-                        final AndroidFuture<Boolean> future = new AndroidFuture<>();
-                        session.reportUsage(
-                                new ReportUsageRequest.Builder(
-                                        getPackageName(), newShortcut.getId()).build(),
-                                mShortcutUser.mExecutor,
-                                result -> future.complete(result.isSuccess()));
-                        return future;
-                    }));
+            runAsSystem(() -> fromAppSearch().thenAccept(session ->
+                    session.reportUsage(new ReportUsageRequest.Builder(
+                            getPackageName(), newShortcut.getId()).build(), mExecutor, result -> {
+                                    if (!result.isSuccess()) {
+                                        Slog.e(TAG, "Failed to report usage via AppSearch. "
+                                                + result.getErrorMessage());
+                                    }
+                            })));
         }
         return deleted;
     }
@@ -470,12 +465,7 @@
      */
     private List<ShortcutInfo> removeOrphans() {
         final List<ShortcutInfo> removeList = new ArrayList<>(1);
-        final String query = String.format("%s OR %s OR %s OR %s",
-                AppSearchShortcutInfo.QUERY_IS_PINNED,
-                AppSearchShortcutInfo.QUERY_IS_DYNAMIC,
-                AppSearchShortcutInfo.QUERY_IS_MANIFEST,
-                AppSearchShortcutInfo.QUERY_IS_CACHED);
-        forEachShortcut(query, si -> {
+        forEachShortcut(si -> {
             if (si.isAlive()) return;
             removeList.add(si);
         });
@@ -484,7 +474,6 @@
                 forceDeleteShortcutInner(removeList.get(i).getId());
             }
         }
-
         return removeList;
     }
 
@@ -493,29 +482,21 @@
      *
      * @return List of shortcuts that actually got removed.
      */
-    public List<ShortcutInfo> deleteAllDynamicShortcuts(boolean ignoreInvisible) {
+    public List<ShortcutInfo> deleteAllDynamicShortcuts() {
         final long now = mShortcutUser.mService.injectCurrentTimeMillis();
-        final String query;
-        if (!ignoreInvisible) {
-            query = AppSearchShortcutInfo.QUERY_IS_DYNAMIC;
-        } else {
-            query = String.format("%s %s",
-                    AppSearchShortcutInfo.QUERY_IS_DYNAMIC,
-                    AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER);
-        }
-        final boolean[] changed = new boolean[1];
-        forEachShortcutMutateIf(query, si -> {
-            if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) {
-                changed[0] = true;
+        boolean changed = false;
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            ShortcutInfo si = mShortcuts.valueAt(i);
+            if (si.isDynamic() && si.isVisibleToPublisher()) {
+                changed = true;
 
                 si.setTimestamp(now);
                 si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
                 si.setRank(0); // It may still be pinned, so clear the rank.
-                return true;
             }
-            return false;
-        });
-        if (changed[0]) {
+        }
+        removeAllShortcutsAsync();
+        if (changed) {
             return removeOrphans();
         }
         return null;
@@ -528,10 +509,11 @@
      * @return The deleted shortcut, or null if it was not actually removed because it is either
      * pinned or cached.
      */
-    public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
+    public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
+            boolean ignorePersistedShortcuts) {
         return deleteOrDisableWithId(
                 shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
-                ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+                ShortcutInfo.DISABLED_REASON_NOT_DISABLED, ignorePersistedShortcuts);
     }
 
     /**
@@ -542,9 +524,9 @@
      * it's still pinned.
      */
     private ShortcutInfo disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
-            int disabledReason) {
+            int disabledReason, boolean ignorePersistedShortcuts) {
         return deleteOrDisableWithId(shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false,
-                ignoreInvisible, disabledReason);
+                ignoreInvisible, disabledReason, ignorePersistedShortcuts);
     }
 
     /**
@@ -560,7 +542,7 @@
         }
         return deleteOrDisableWithId(
                 shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
-                ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+                ShortcutInfo.DISABLED_REASON_NOT_DISABLED, /*ignorePersistedShortcuts=*/ false);
     }
 
     /**
@@ -574,7 +556,8 @@
             int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
             int disabledReason) {
         final ShortcutInfo deleted = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
-                overrideImmutable, ignoreInvisible, disabledReason);
+                overrideImmutable, ignoreInvisible, disabledReason,
+                /*ignorePersistedShortcuts=*/ false);
 
         // If disabled id still exists, it is pinned and we need to update the disabled message.
         mutateShortcut(shortcutId, null, disabled -> {
@@ -593,7 +576,8 @@
 
     @Nullable
     private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
-            boolean overrideImmutable, boolean ignoreInvisible, int disabledReason) {
+            boolean overrideImmutable, boolean ignoreInvisible, int disabledReason,
+            boolean ignorePersistedShortcuts) {
         Preconditions.checkState(
                 (disable == (disabledReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED)),
                 "disable and disabledReason disagree: " + disable + " vs " + disabledReason);
@@ -606,8 +590,10 @@
         if (!overrideImmutable) {
             ensureNotImmutable(oldShortcut, /*ignoreInvisible=*/ true);
         }
+        if (!ignorePersistedShortcuts) {
+            removeShortcutAsync(shortcutId);
+        }
         if (oldShortcut.isPinned() || oldShortcut.isCached()) {
-
             mutateShortcut(oldShortcut.getId(), oldShortcut, si -> {
                 si.setRank(0);
                 si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
@@ -672,21 +658,19 @@
             pinnedShortcuts.addAll(pinned);
         });
         // Then, update the pinned state if necessary.
-        final List<ShortcutInfo> pinned = getShortcutById(pinnedShortcuts);
+        final List<ShortcutInfo> pinned = findAll(pinnedShortcuts);
         if (pinned != null) {
             pinned.forEach(si -> {
                 if (!si.isPinned()) {
                     si.addFlags(ShortcutInfo.FLAG_PINNED);
                 }
             });
-            saveShortcut(pinned);
         }
-        forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_IS_PINNED, si -> {
+        forEachShortcutMutate(si -> {
             if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
                 si.clearFlags(ShortcutInfo.FLAG_PINNED);
-                return true;
+                return;
             }
-            return false;
         });
 
         // Lastly, remove the ones that are no longer pinned, cached nor dynamic.
@@ -777,9 +761,9 @@
     /**
      * Find all shortcuts that match {@code query}.
      */
-    public void findAll(@NonNull List<ShortcutInfo> result, @Nullable String query,
+    public void findAll(@NonNull List<ShortcutInfo> result,
             @Nullable Predicate<ShortcutInfo> filter, int cloneFlag) {
-        findAll(result, query, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
+        findAll(result, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
     }
 
     /**
@@ -790,7 +774,7 @@
      * adjusted for the caller too.
      */
     public void findAll(@NonNull List<ShortcutInfo> result,
-            @Nullable String query, @Nullable Predicate<ShortcutInfo> filter, int cloneFlag,
+            @Nullable Predicate<ShortcutInfo> filter, int cloneFlag,
             @Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
         if (getPackageInfo().isShadow()) {
             // Restored and the app not installed yet, so don't return any.
@@ -802,9 +786,8 @@
         final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
                 : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
                         .getPinnedShortcutIds(getPackageName(), getPackageUserId());
-        forEachShortcut(query == null ? "" : query, si ->
-                filter(result, filter, cloneFlag, callingLauncher, pinnedByCallerSet,
-                        getPinnedByAnyLauncher, si));
+        forEachShortcut(si -> filter(result, filter, cloneFlag, callingLauncher, pinnedByCallerSet,
+                getPinnedByAnyLauncher, si));
     }
 
     /**
@@ -837,35 +820,12 @@
         final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
                 : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
                         .getPinnedShortcutIds(getPackageName(), getPackageUserId());
-        final List<ShortcutInfo> shortcuts = getShortcutById(ids);
-        if (shortcuts != null) {
-            for (ShortcutInfo si : shortcuts) {
-                filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
-                        getPinnedByAnyLauncher, si);
-            }
+        for (ShortcutInfo si : mShortcuts.values()) {
+            filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
+                    getPinnedByAnyLauncher, si);
         }
     }
 
-    /**
-     * Find all pinned shortcuts that match {@code query}.
-     */
-    public void findAllPinned(@NonNull List<ShortcutInfo> result,
-            @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
-            @Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
-        if (getPackageInfo().isShadow()) {
-            // Restored and the app not installed yet, so don't return any.
-            return;
-        }
-        final ShortcutService s = mShortcutUser.mService;
-
-        // Set of pinned shortcuts by the calling launcher.
-        final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
-                : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
-                        .getPinnedShortcutIds(getPackageName(), getPackageUserId());
-        mShortcuts.values().forEach(si -> filter(result, query, cloneFlag, callingLauncher,
-                pinnedByCallerSet, getPinnedByAnyLauncher, si));
-    }
-
     private void filter(@NonNull final List<ShortcutInfo> result,
             @Nullable final Predicate<ShortcutInfo> query, final int cloneFlag,
             @Nullable final String callingLauncher,
@@ -930,8 +890,8 @@
 
         // Get the list of all dynamic shortcuts in this package.
         final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
-        findAll(shortcuts, AppSearchShortcutInfo.QUERY_IS_NON_MANIFEST_VISIBLE,
-                ShortcutInfo::isNonManifestVisible, ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
+        findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+                ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
 
         final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
         for (int i = 0; i < shortcuts.size(); i++) {
@@ -975,8 +935,8 @@
 
         // Get the list of all dynamic shortcuts in this package
         final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
-        findAll(shortcuts, AppSearchShortcutInfo.QUERY_IS_NON_MANIFEST_VISIBLE,
-                ShortcutInfo::isNonManifestVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+        findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+                ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
 
         int sharingShortcutCount = 0;
         for (int i = 0; i < shortcuts.size(); i++) {
@@ -1129,53 +1089,37 @@
                     getPackageInfo().getVersionCode(), pi.getLongVersionCode()));
         }
         getPackageInfo().updateFromPackageInfo(pi);
-        if (isAppSearchEnabled()) {
-            // Save the states in memory and resume package rescan when needed
-            mRescanRequired = true;
-            mIsNewApp = isNewApp;
-            mManifestShortcuts = newManifestShortcutList;
-        } else {
-            rescanPackage(isNewApp, newManifestShortcutList);
-        }
-        return true; // true means changed.
-    }
-
-    private void rescanPackage(
-            final boolean isNewApp, @NonNull final List<ShortcutInfo> newManifestShortcutList) {
-        final ShortcutService s = mShortcutUser.mService;
 
         final long newVersionCode = getPackageInfo().getVersionCode();
 
         // See if there are any shortcuts that were prevented restoring because the app was of a
         // lower version, and re-enable them.
         {
-            forEachShortcutMutateIf(
-                    AppSearchShortcutInfo.QUERY_DISABLED_REASON_VERSION_LOWER, si -> {
-                    if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
-                        return false;
+            forEachShortcutMutate(si -> {
+                if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
+                    return;
+                }
+                if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
+                    if (ShortcutService.DEBUG) {
+                        Slog.d(TAG,
+                                String.format(
+                                        "Shortcut %s require version %s, still not restored.",
+                                        si.getId(),
+                                        getPackageInfo().getBackupSourceVersionCode()));
                     }
-                    if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
-                        if (ShortcutService.DEBUG) {
-                            Slog.d(TAG,
-                                    String.format(
-                                            "Shortcut %s require version %s, still not restored.",
-                                            si.getId(),
-                                            getPackageInfo().getBackupSourceVersionCode()));
-                        }
-                        return false;
-                    }
-                    Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
-                    si.clearFlags(ShortcutInfo.FLAG_DISABLED);
-                    si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
-                    return true;
-                });
+                    return;
+                }
+                Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
+                si.clearFlags(ShortcutInfo.FLAG_DISABLED);
+                si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+            });
         }
 
         // For existing shortcuts, update timestamps if they have any resources.
         // Also check if shortcuts' activities are still main activities.  Otherwise, disable them.
         if (!isNewApp) {
             final Resources publisherRes = getPackageResources();
-            forEachShortcutMutateIf(si -> {
+            forEachShortcutMutate(si -> {
                 // Disable dynamic shortcuts whose target activity is gone.
                 if (si.isDynamic()) {
                     if (si.getActivity() == null) {
@@ -1187,15 +1131,16 @@
                                 "%s is no longer main activity. Disabling shorcut %s.",
                                 getPackageName(), si.getId()));
                         if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
-                                ShortcutInfo.DISABLED_REASON_APP_CHANGED) != null) {
-                            return false; // Actually removed.
+                                ShortcutInfo.DISABLED_REASON_APP_CHANGED,
+                                /*ignorePersistedShortcuts*/ false) != null) {
+                            return;
                         }
                         // Still pinned, so fall-through and possibly update the resources.
                     }
                 }
 
                 if (!si.hasAnyResources() || publisherRes == null) {
-                    return false;
+                    return;
                 }
 
                 if (!si.isOriginallyFromManifest()) {
@@ -1206,7 +1151,6 @@
                 // from resource names.  (We don't allow resource strings for
                 // non-manifest at the moment, but icons can still be resources.)
                 si.setTimestamp(s.injectCurrentTimeMillis());
-                return true;
             });
         }
 
@@ -1222,7 +1166,8 @@
         // This will send a notification to the launcher, and also save .
         // TODO: List changed and removed manifest shortcuts and pass to packageShortcutsChanged()
         s.packageShortcutsChanged(getPackageName(), getPackageUserId(), null, null);
-        mManifestShortcuts = null;
+
+        return true;
     }
 
     private boolean publishManifestShortcuts(List<ShortcutInfo> newManifestShortcutList) {
@@ -1297,7 +1242,8 @@
 
                 final String id = toDisableList.valueAt(i);
 
-                disableWithId(id, /* disable message =*/ null, /* disable message resid */ 0,
+                disableWithId(id, /* disable message =*/ null,
+                        /* disable message resid */ 0,
                         /* overrideImmutable=*/ true, /*ignoreInvisible=*/ false,
                         ShortcutInfo.DISABLED_REASON_APP_CHANGED);
             }
@@ -1338,7 +1284,8 @@
                     service.wtf("Found manifest shortcuts in excess list.");
                     continue;
                 }
-                deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
+                deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true,
+                        /*ignorePersistedShortcuts=*/ true);
             }
         }
 
@@ -1494,12 +1441,11 @@
         final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
 
         if (publisherRes != null) {
-            forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_HAS_STRING_RESOURCE, si -> {
-                if (!si.hasStringResources()) return false;
+            forEachShortcutMutate(si -> {
+                if (!si.hasStringResources()) return;
                 si.resolveResourceStrings(publisherRes);
                 si.setTimestamp(s.injectCurrentTimeMillis());
                 changedShortcuts.add(si);
-                return true;
             });
         }
         if (!CollectionUtils.isEmpty(changedShortcuts)) {
@@ -1549,13 +1495,11 @@
         final long now = s.injectCurrentTimeMillis();
 
         // First, clear ranks for floating shortcuts.
-        forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_IS_FLOATING_AND_HAS_RANK, si -> {
+        forEachShortcutMutate(si -> {
             if (si.isFloating() && si.getRank() != 0) {
                 si.setTimestamp(now);
                 si.setRank(0);
-                return true;
             }
-            return false;
         });
 
         // Then adjust ranks.  Ranks are unique for each activity, so we first need to sort
@@ -1581,7 +1525,7 @@
                 }
                 // At this point, it must be dynamic.
                 if (!si.isDynamic()) {
-                    s.wtf("Non-dynamic shortcut found.");
+                    s.wtf("Non-dynamic shortcut found. " + si.toInsecureString());
                     continue;
                 }
                 final int thisRank = rank++;
@@ -1745,13 +1689,10 @@
         ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
         ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
         if (!forBackup) {
-            /**
-             * Schema version should not be included in the backup because:
-             * 1. Schemas in AppSearch are created from scratch on new device
-             * 2. Shortcuts are restored from xml file (as opposed to from AppSearch) on new device
-             */
-            ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsInitilized)
-                    ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
+            synchronized (mLock) {
+                ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate)
+                        ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
+            }
         }
         getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
 
@@ -1763,6 +1704,8 @@
             for (int j = 0; j < shareTargetSize; j++) {
                 mShareTargets.get(j).saveToXml(out);
             }
+            saveShortcutsAsync(mShortcuts.values().stream().filter(ShortcutInfo::usesQuota)
+                    .collect(Collectors.toList()));
         }
 
         out.endTag(null, TAG_ROOT);
@@ -1943,8 +1886,10 @@
         final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
                 shortcutUser.getUserId(), packageName);
 
-        ret.mIsInitilized = ShortcutService.parseIntAttribute(parser, ATTR_SCHEMA_VERSON, 0)
-                == AppSearchShortcutInfo.SCHEMA_VERSION;
+        synchronized (ret.mLock) {
+            ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
+                    parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
+        }
         ret.mApiCallCount =
                 ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
         ret.mLastResetTime =
@@ -2297,8 +2242,15 @@
         } else {
             mPackageIdentifiers.remove(packageName);
         }
-        awaitInAppSearch(true, "Update visibility",
-                session -> AndroidFuture.completedFuture(true));
+        synchronized (mLock) {
+            mIsAppSearchSchemaUpToDate = false;
+        }
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            fromAppSearch();
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
     }
 
     void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut,
@@ -2325,148 +2277,15 @@
 
     private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
         Objects.requireNonNull(shortcuts);
-        if (!isAppSearchEnabled()) {
-            // If AppSearch isn't enabled, save it in memory and we are done.
-            for (ShortcutInfo si : shortcuts) {
-                mShortcuts.put(si.getId(), si);
-            }
-            return;
+        for (ShortcutInfo si : shortcuts) {
+            mShortcuts.put(si.getId(), si);
         }
-        // Otherwise, save pinned shortcuts in memory.
-        shortcuts.forEach(si -> {
-            if (si.isPinned()) {
-                mShortcuts.put(si.getId(), si);
-            } else {
-                mShortcuts.remove(si.getId());
-            }
-        });
-        // Then proceed to app search.
-        saveToAppSearch(shortcuts);
-    }
-
-    private void saveToAppSearch(@NonNull final Collection<ShortcutInfo> shortcuts) {
-        Objects.requireNonNull(shortcuts);
-        if (!isAppSearchEnabled() || shortcuts.isEmpty()) {
-            // No need to invoke AppSearch when there's nothing to save.
-            return;
-        }
-        if (ShortcutService.DEBUG_REBOOT) {
-            Slog.d(TAG, "Saving shortcuts for user=" + mShortcutUser.getUserId()
-                    + " pkg=" + getPackageName() + " ids=["
-                    + shortcuts.stream().map(ShortcutInfo::getId)
-                    .collect(Collectors.joining(",")) + "]");
-        }
-        awaitInAppSearch("Saving shortcuts", session -> {
-            final AndroidFuture<Boolean> future = new AndroidFuture<>();
-            session.put(new PutDocumentsRequest.Builder()
-                            .addGenericDocuments(
-                                    AppSearchShortcutInfo.toGenericDocuments(shortcuts))
-                            .build(),
-                    mShortcutUser.mExecutor,
-                    result -> {
-                        if (!result.isSuccess()) {
-                            for (AppSearchResult<Void> k : result.getFailures().values()) {
-                                Slog.e(TAG, k.getErrorMessage());
-                            }
-                            future.completeExceptionally(new RuntimeException(
-                                    "Failed to save shortcuts"));
-                            return;
-                        }
-                        future.complete(true);
-                    });
-            return future;
-        });
-    }
-
-    /**
-     * Removes shortcuts from AppSearch.
-     */
-    void removeShortcuts() {
-        if (!isAppSearchEnabled()) {
-            return;
-        }
-        awaitInAppSearch("Removing all shortcuts from " + getPackageName(), session -> {
-            final AndroidFuture<Boolean> future = new AndroidFuture<>();
-            session.remove("", getSearchSpec(), mShortcutUser.mExecutor, result -> {
-                if (!result.isSuccess()) {
-                    future.completeExceptionally(
-                            new RuntimeException(result.getErrorMessage()));
-                    return;
-                }
-                future.complete(true);
-            });
-            return future;
-        });
-    }
-
-    private void removeShortcut(@NonNull final String id) {
-        Objects.requireNonNull(id);
-        mShortcuts.remove(id);
-        if (!isAppSearchEnabled()) {
-            return;
-        }
-        awaitInAppSearch("Removing shortcut with id=" + id, session -> {
-            final AndroidFuture<Boolean> future = new AndroidFuture<>();
-            session.remove(
-                    new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(id).build(),
-                    mShortcutUser.mExecutor, result -> {
-                        if (!result.isSuccess()) {
-                            final Map<String, AppSearchResult<Void>> failures =
-                                    result.getFailures();
-                            for (String key : failures.keySet()) {
-                                Slog.e(TAG, "Failed deleting " + key + ", error message:"
-                                        + failures.get(key).getErrorMessage());
-                            }
-                            future.completeExceptionally(new RuntimeException(
-                                    "Failed to delete shortcut: " + id));
-                            return;
-                        }
-                        future.complete(true);
-                    });
-            return future;
-        });
     }
 
     @Nullable
-    private List<ShortcutInfo> getShortcutById(@NonNull final Collection<String> ids) {
-        final List<String> shortcutIds = new ArrayList<>(1);
-        for (String id : ids) {
-            if (id != null) {
-                shortcutIds.add(id);
-            }
-        }
-        if (!isAppSearchEnabled()) {
-            final List<ShortcutInfo> ret = new ArrayList<>(1);
-            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-                ShortcutInfo si = mShortcuts.valueAt(i);
-                if (shortcutIds.contains(si.getId())) {
-                    ret.add(si);
-                }
-            }
-            return ret;
-        }
-        if (ShortcutService.DEBUG_REBOOT) {
-            Slog.d(TAG, "Getting shortcuts for user=" + mShortcutUser.getUserId()
-                    + " pkg=" + getPackageName() + " ids: [" + String.join(",", ids) + "]");
-        }
-        return awaitInAppSearch("Getting shortcut by id", session -> {
-            final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
-            session.getByDocumentId(
-                    new GetByDocumentIdRequest.Builder(getPackageName())
-                            .addIds(shortcutIds).build(),
-                    mShortcutUser.mExecutor,
-                    results -> {
-                        final List<ShortcutInfo> ret = new ArrayList<>(1);
-                        Map<String, GenericDocument> documents = results.getSuccesses();
-                        for (GenericDocument doc : documents.values()) {
-                            final ShortcutInfo info = new AppSearchShortcutInfo(doc)
-                                    .toShortcutInfo(mShortcutUser.getUserId());
-                            ret.add(info);
-                        }
-                        future.complete(ret);
-                    });
-            return future;
-        });
+    List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) {
+        return ids.stream().map(mShortcuts::get)
+                .filter(Objects::nonNull).collect(Collectors.toList());
     }
 
     private void forEachShortcut(@NonNull final Consumer<ShortcutInfo> cb) {
@@ -2482,40 +2301,9 @@
     }
 
     private void forEachShortcutMutate(@NonNull final Consumer<ShortcutInfo> cb) {
-        forEachShortcutMutateIf(si -> {
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            ShortcutInfo si = mShortcuts.valueAt(i);
             cb.accept(si);
-            return true;
-        });
-    }
-
-    private void forEachShortcutMutateIf(@NonNull final Function<ShortcutInfo, Boolean> cb) {
-        forEachShortcutMutateIf("", cb);
-    }
-
-    private void forEachShortcutMutateIf(@NonNull final String query,
-            @NonNull final Function<ShortcutInfo, Boolean> cb) {
-        if (!isAppSearchEnabled()) {
-            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-                ShortcutInfo si = mShortcuts.valueAt(i);
-                cb.apply(si);
-            }
-            return;
-        }
-        if (ShortcutService.DEBUG_REBOOT) {
-            Slog.d(TAG, "Changing shortcuts for user=" + mShortcutUser.getUserId()
-                    + " pkg=" + getPackageName());
-        }
-        final SearchResults res = awaitInAppSearch("Mutating shortcuts", session ->
-                AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
-        if (res == null) return;
-        List<ShortcutInfo> shortcuts = getNextPage(res);
-        while (!shortcuts.isEmpty()) {
-            final List<ShortcutInfo> changed = new ArrayList<>(1);
-            for (ShortcutInfo si : shortcuts) {
-                if (cb.apply(si)) changed.add(si);
-            }
-            saveShortcut(changed);
-            shortcuts = getNextPage(res);
         }
     }
 
@@ -2526,114 +2314,10 @@
 
     private void forEachShortcutStopWhen(
             @NonNull final String query, @NonNull final Function<ShortcutInfo, Boolean> cb) {
-        if (!isAppSearchEnabled()) {
-            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-                final ShortcutInfo si = mShortcuts.valueAt(i);
-                if (cb.apply(si)) {
-                    return;
-                }
-            }
-            return;
-        }
-        if (ShortcutService.DEBUG_REBOOT) {
-            Slog.d(TAG, "Iterating shortcuts for user=" + mShortcutUser.getUserId()
-                    + " pkg=" + getPackageName());
-        }
-        final SearchResults res = awaitInAppSearch("Iterating shortcuts", session ->
-                AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
-        if (res == null) return;
-        List<ShortcutInfo> shortcuts = getNextPage(res);
-        while (!shortcuts.isEmpty()) {
-            for (ShortcutInfo si : shortcuts) {
-                if (cb.apply(si)) return;
-            }
-            shortcuts = getNextPage(res);
-        }
-    }
-
-    private List<ShortcutInfo> getNextPage(@NonNull final SearchResults res) {
-        if (ShortcutService.DEBUG_REBOOT) {
-            Slog.d(TAG, "Get next page for search result for user=" + mShortcutUser.getUserId()
-                    + " pkg=" + getPackageName());
-        }
-        final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
-        final List<ShortcutInfo> ret = new ArrayList<>();
-        final long callingIdentity = Binder.clearCallingIdentity();
-        try {
-            res.getNextPage(mShortcutUser.mExecutor, nextPage -> {
-                if (!nextPage.isSuccess()) {
-                    future.complete(ret);
-                    return;
-                }
-                final List<SearchResult> results = nextPage.getResultValue();
-                if (results.isEmpty()) {
-                    future.complete(ret);
-                    return;
-                }
-                final List<ShortcutInfo> page = new ArrayList<>(results.size());
-                for (SearchResult result : results) {
-                    final ShortcutInfo si = new AppSearchShortcutInfo(result.getGenericDocument())
-                            .toShortcutInfo(mShortcutUser.getUserId());
-                    page.add(si);
-                }
-                ret.addAll(page);
-                future.complete(ret);
-            });
-            return ConcurrentUtils.waitForFutureNoInterrupt(future,
-                    "Getting next batch of shortcuts");
-        } finally {
-            Binder.restoreCallingIdentity(callingIdentity);
-        }
-    }
-
-    @Nullable
-    private <T> T awaitInAppSearch(
-            @NonNull final String description,
-            @NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
-        return awaitInAppSearch(false, description, cb);
-    }
-
-    @Nullable
-    private <T> T awaitInAppSearch(
-            final boolean forceReset,
-            @NonNull final String description,
-            @NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
-        if (!isAppSearchEnabled()) {
-            throw new IllegalStateException(
-                    "awaitInAppSearch called when app search integration is disabled");
-        }
-        synchronized (mLock) {
-            final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
-            final long callingIdentity = Binder.clearCallingIdentity();
-            final AppSearchManager.SearchContext searchContext =
-                    new AppSearchManager.SearchContext.Builder(getPackageName()).build();
-            try (AppSearchSession session = ConcurrentUtils.waitForFutureNoInterrupt(
-                    mShortcutUser.getAppSearch(searchContext), "Resetting app search")) {
-                StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
-                        .detectAll()
-                        .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
-                        .build());
-                final boolean wasInitialized = mIsInitilized;
-                if (!wasInitialized || forceReset) {
-                    ConcurrentUtils.waitForFutureNoInterrupt(
-                            setupSchema(session), "Setting up schema");
-                }
-                mIsInitilized = true;
-                if (!wasInitialized) {
-                    restoreParsedShortcuts(false);
-                }
-                if (mRescanRequired) {
-                    mRescanRequired = false;
-                    rescanPackage(mIsNewApp, mManifestShortcuts);
-                }
-                return ConcurrentUtils.waitForFutureNoInterrupt(cb.apply(session), description);
-            } catch (Exception e) {
-                Slog.e(TAG, "Failed to initiate app search for shortcut package "
-                        + getPackageName() + " user " + mShortcutUser.getUserId(), e);
-                return null;
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-                StrictMode.setThreadPolicy(oldPolicy);
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            final ShortcutInfo si = mShortcuts.valueAt(i);
+            if (cb.apply(si)) {
+                return;
             }
         }
     }
@@ -2657,7 +2341,7 @@
         }
         final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
         session.setSchema(
-                schemaBuilder.build(), mShortcutUser.mExecutor, mShortcutUser.mExecutor, result -> {
+                schemaBuilder.build(), mExecutor, mShortcutUser.mExecutor, result -> {
             if (!result.isSuccess()) {
                 future.completeExceptionally(
                         new IllegalArgumentException(result.getErrorMessage()));
@@ -2677,29 +2361,6 @@
                 .build();
     }
 
-    /**
-     * Replace shortcuts parsed from xml file.
-     */
-    void restoreParsedShortcuts() {
-        restoreParsedShortcuts(true);
-    }
-
-    private void restoreParsedShortcuts(final boolean replace) {
-        if (ShortcutService.DEBUG_REBOOT) {
-            if (replace) {
-                Slog.d(TAG, "Replacing all shortcuts with the ones parsed from xml for user="
-                        + mShortcutUser.getUserId() + " pkg=" + getPackageName());
-            } else {
-                Slog.d(TAG, "Restoring pinned shortcuts from xml for user="
-                        + mShortcutUser.getUserId() + " pkg=" + getPackageName());
-            }
-        }
-        if (replace) {
-            removeShortcuts();
-        }
-        saveToAppSearch(mShortcuts.values());
-    }
-
     private boolean verifyRanksSequential(List<ShortcutInfo> list) {
         boolean failed = false;
 
@@ -2713,4 +2374,133 @@
         }
         return failed;
     }
+
+    // Async Operations
+
+    /**
+     * Removes all shortcuts from AppSearch.
+     */
+    void removeAllShortcutsAsync() {
+        if (!isAppSearchEnabled()) {
+            return;
+        }
+        runAsSystem(() -> fromAppSearch().thenAccept(session ->
+                session.remove("", getSearchSpec(), mShortcutUser.mExecutor, result -> {
+                    if (!result.isSuccess()) {
+                        Slog.e(TAG, "Failed to remove shortcuts from AppSearch. "
+                                + result.getErrorMessage());
+                    }
+                })));
+    }
+
+    private void removeShortcutAsync(@NonNull final String... id) {
+        Objects.requireNonNull(id);
+        removeShortcutAsync(Arrays.asList(id));
+    }
+
+    private void removeShortcutAsync(@NonNull final Collection<String> ids) {
+        if (!isAppSearchEnabled()) {
+            return;
+        }
+        runAsSystem(() -> fromAppSearch().thenAccept(session ->
+                session.remove(
+                        new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(ids).build(),
+                        mShortcutUser.mExecutor, result -> {
+                            if (!result.isSuccess()) {
+                                final Map<String, AppSearchResult<Void>> failures =
+                                        result.getFailures();
+                                for (String key : failures.keySet()) {
+                                    Slog.e(TAG, "Failed deleting " + key + ", error message:"
+                                            + failures.get(key).getErrorMessage());
+                                }
+                            }
+                        })));
+    }
+
+    private void saveShortcutsAsync(
+            @NonNull final Collection<ShortcutInfo> shortcuts) {
+        Objects.requireNonNull(shortcuts);
+        if (!isAppSearchEnabled() || shortcuts.isEmpty()) {
+            // No need to invoke AppSearch when there's nothing to save.
+            return;
+        }
+        if (ShortcutService.DEBUG_REBOOT) {
+            Slog.d(TAG, "Saving shortcuts async for user=" + mShortcutUser.getUserId()
+                    + " pkg=" + getPackageName() + " ids=["
+                    + shortcuts.stream().map(ShortcutInfo::getId)
+                    .collect(Collectors.joining(",")) + "]");
+        }
+        runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+            if (shortcuts.isEmpty()) {
+                return;
+            }
+            session.put(new PutDocumentsRequest.Builder()
+                            .addGenericDocuments(
+                                    AppSearchShortcutInfo.toGenericDocuments(shortcuts))
+                            .build(),
+                    mShortcutUser.mExecutor,
+                    result -> {
+                        if (!result.isSuccess()) {
+                            for (AppSearchResult<Void> k : result.getFailures().values()) {
+                                Slog.e(TAG, k.getErrorMessage());
+                            }
+                        }
+                    });
+        }));
+    }
+
+    @VisibleForTesting
+    void getTopShortcutsFromPersistence(AndroidFuture<List<ShortcutInfo>> cb) {
+        runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+            SearchResults res = session.search("", getSearchSpec());
+            res.getNextPage(mShortcutUser.mExecutor, results -> {
+                if (!results.isSuccess()) {
+                    cb.completeExceptionally(new IllegalStateException(results.getErrorMessage()));
+                    return;
+                }
+                cb.complete(results.getResultValue().stream()
+                        .map(SearchResult::getGenericDocument)
+                        .map(AppSearchShortcutInfo::new)
+                        .map(si -> si.toShortcutInfo(mShortcutUser.getUserId()))
+                        .collect(Collectors.toList()));
+            });
+        }));
+    }
+
+    @NonNull
+    private AndroidFuture<AppSearchSession> fromAppSearch() {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+        final AppSearchManager.SearchContext searchContext =
+                new AppSearchManager.SearchContext.Builder(getPackageName()).build();
+        AndroidFuture<AppSearchSession> future = null;
+        try {
+            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+                    .detectAll()
+                    .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
+                    .build());
+            future = mShortcutUser.getAppSearch(searchContext);
+            synchronized (mLock) {
+                if (!mIsAppSearchSchemaUpToDate) {
+                    future = future.thenCompose(this::setupSchema);
+                }
+                mIsAppSearchSchemaUpToDate = true;
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to invoke app search pkg="
+                    + getPackageName() + " user=" + mShortcutUser.getUserId(), e);
+            Objects.requireNonNull(future).completeExceptionally(e);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+        return Objects.requireNonNull(future);
+    }
+
+    private void runAsSystem(@NonNull final Runnable fn) {
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            fn.run();
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index e21c9c2..c1f57f9 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -467,7 +467,7 @@
             launcher.attemptToRestoreIfNeededAndSave();
             if (launcher.hasPinned(original)) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Shortcut " + original + " already pinned.");                       // This too.
+                    Slog.d(TAG, "Shortcut " + original + " already pinned.");   // This too.
                 }
                 return true;
             }
@@ -517,7 +517,8 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
                 }
-                ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
+                ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false,
+                        /*wasPushedOut=*/ false);
             }
 
             ps.adjustRanks(); // Shouldn't be needed, but just in case.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 687a165..85b7435 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1965,13 +1965,12 @@
 
             ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
             ps.findAll(cachedOrPinned,
-                    AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED,
                     (ShortcutInfo si) -> si.isVisibleToPublisher()
                             && si.isDynamic() && (si.isCached() || si.isPinned()),
                     ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
 
             // First, remove all un-pinned and non-cached; dynamic shortcuts
-            removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+            removedShortcuts = ps.deleteAllDynamicShortcuts();
 
             // Then, add/update all.  We need to make sure to take over "pinned" flag.
             for (int i = 0; i < size; i++) {
@@ -2381,7 +2380,8 @@
                 if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
                     continue;
                 }
-                ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
+                ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true,
+                        /*wasPushedOut*/ false);
                 if (removed == null) {
                     if (changedShortcuts == null) {
                         changedShortcuts = new ArrayList<>(1);
@@ -2412,11 +2412,10 @@
                     userId);
             // Dynamic shortcuts that are either cached or pinned will not get deleted.
             ps.findAll(changedShortcuts,
-                    AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED,
                     (ShortcutInfo si) -> si.isVisibleToPublisher()
                             && si.isDynamic() && (si.isCached() || si.isPinned()),
                     ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
-            removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+            removedShortcuts = ps.deleteAllDynamicShortcuts();
             changedShortcuts = prepareChangedShortcuts(
                     changedShortcuts, null, removedShortcuts, ps);
         }
@@ -2475,10 +2474,8 @@
                     | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
                     | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
                     | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
-            final String query = AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER + " "
-                    + createQuery(matchDynamic, matchPinned, matchManifest, matchCached);
             return getShortcutsWithQueryLocked(
-                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, query,
+                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
                     (ShortcutInfo si) ->
                             si.isVisibleToPublisher()
                                     && (si.getFlags() & shortcutFlags) != 0);
@@ -2542,13 +2539,12 @@
 
     @GuardedBy("mLock")
     private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
-            @UserIdInt int userId, int cloneFlags, @NonNull final String query,
-            @NonNull Predicate<ShortcutInfo> filter) {
+            @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> filter) {
 
         final ArrayList<ShortcutInfo> ret = new ArrayList<>();
 
         final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
-        ps.findAll(ret, query, filter, cloneFlags);
+        ps.findAll(ret, filter, cloneFlags);
         return new ParceledListSlice<>(setReturnedByServer(ret));
     }
 
@@ -2955,27 +2951,14 @@
                     ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER) != 0);
             queryFlags |= (getPinnedByAnyLauncher ? ShortcutQuery.FLAG_MATCH_PINNED : 0);
 
-            final boolean matchPinnedOnly =
-                    ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0)
-                            && ((queryFlags & ShortcutQuery.FLAG_MATCH_CACHED) == 0)
-                            && ((queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) == 0)
-                            && ((queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) == 0);
-
             final Predicate<ShortcutInfo> filter = getFilterFromQuery(ids, locusIds, changedSince,
                     componentName, queryFlags, getPinnedByAnyLauncher);
-            if (matchPinnedOnly) {
-                p.findAllPinned(ret, filter, cloneFlag, callingPackage, launcherUserId,
-                        getPinnedByAnyLauncher);
-            } else if (ids != null && !ids.isEmpty()) {
+            if (ids != null && !ids.isEmpty()) {
                 p.findAllByIds(ret, ids, filter, cloneFlag, callingPackage, launcherUserId,
                         getPinnedByAnyLauncher);
             } else {
-                final boolean matchDynamic = (queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
-                final boolean matchPinned = (queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
-                final boolean matchManifest = (queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
-                final boolean matchCached = (queryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
-                p.findAll(ret, createQuery(matchDynamic, matchPinned, matchManifest, matchCached),
-                        filter, cloneFlag, callingPackage, launcherUserId, getPinnedByAnyLauncher);
+                p.findAll(ret, filter, cloneFlag, callingPackage, launcherUserId,
+                        getPinnedByAnyLauncher);
             }
         }
 
@@ -3090,8 +3073,7 @@
                 if (sp != null) {
                     // List the shortcuts that are pinned only, these will get removed.
                     removedShortcuts = new ArrayList<>();
-                    sp.findAll(removedShortcuts, AppSearchShortcutInfo.QUERY_IS_VISIBLE_PINNED_ONLY,
-                            (ShortcutInfo si) -> si.isVisibleToPublisher()
+                    sp.findAll(removedShortcuts, (ShortcutInfo si) -> si.isVisibleToPublisher()
                                     && si.isPinned() && !si.isCached() && !si.isDynamic()
                                     && !si.isDeclaredInManifest(),
                             ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO,
@@ -3183,8 +3165,7 @@
 
                     if (doCache) {
                         if (si.isLongLived()) {
-                            sp.mutateShortcut(si.getId(), si,
-                                    shortcut -> shortcut.addFlags(cacheFlags));
+                            si.addFlags(cacheFlags);
                             if (changedShortcuts == null) {
                                 changedShortcuts = new ArrayList<>(1);
                             }
@@ -3195,21 +3176,20 @@
                         }
                     } else {
                         ShortcutInfo removed = null;
-                        sp.mutateShortcut(si.getId(), si, shortcut ->
-                                shortcut.clearFlags(cacheFlags));
+                        si.clearFlags(cacheFlags);
                         if (!si.isDynamic() && !si.isCached()) {
                             removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
                         }
-                        if (removed != null) {
-                            if (removedShortcuts == null) {
-                                removedShortcuts = new ArrayList<>(1);
-                            }
-                            removedShortcuts.add(removed);
-                        } else {
+                        if (removed == null) {
                             if (changedShortcuts == null) {
                                 changedShortcuts = new ArrayList<>(1);
                             }
                             changedShortcuts.add(si);
+                        } else {
+                            if (removedShortcuts == null) {
+                                removedShortcuts = new ArrayList<>(1);
+                            }
+                            removedShortcuts.add(removed);
                         }
                     }
                 }
@@ -5084,8 +5064,7 @@
         synchronized (mLock) {
             final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
             if (pkg == null) return;
-
-            pkg.mutateShortcut(shortcutId, null, cb);
+            cb.accept(pkg.findShortcutById(shortcutId));
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index e66cb03..408f045 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -186,7 +186,7 @@
         final ShortcutPackage removed = mPackages.remove(packageName);
 
         if (removed != null) {
-            removed.removeShortcuts();
+            removed.removeAllShortcutsAsync();
         }
         mService.cleanupBitmapsForPackage(mUserId, packageName);
 
@@ -577,7 +577,7 @@
                 Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
                         + " Existing non-manifeset shortcuts will be overwritten.");
             }
-            sp.restoreParsedShortcuts();
+            sp.removeAllShortcutsAsync();
             addPackage(sp);
             restoredPackages[0]++;
             restoredShortcuts[0] += sp.getShortcutCount();
@@ -714,6 +714,7 @@
                 .setSubtype(totalSharingShortcutCount));
     }
 
+    @NonNull
     AndroidFuture<AppSearchSession> getAppSearch(
             @NonNull final AppSearchManager.SearchContext searchContext) {
         final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index b49c8da..12e644d 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -136,7 +136,7 @@
 
         final Settings.VersionInfo ver;
         final List<PackageSetting> packages;
-        final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
+        final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
         synchronized (mPm.mLock) {
             ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
             packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
@@ -147,7 +147,7 @@
             synchronized (mPm.mInstallLock) {
                 final AndroidPackage pkg;
                 try {
-                    pkg = scanPackageHelper.scanPackageTracedLI(
+                    pkg = installPackageHelper.scanSystemPackageTracedLI(
                             ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
                     loaded.add(pkg);
 
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 1155132..ce5a2e8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -53,8 +53,8 @@
     SharedUserSetting getSharedUser();
 
     // TODO: Remove this in favor of boolean APIs
-    int getPkgFlags();
-    int getPkgPrivateFlags();
+    int getFlags();
+    int getPrivateFlags();
 
     @NonNull
     SparseArray<? extends PackageUserStateInternal> getUserStates();
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 ab61ef4..0394d4c 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3992,7 +3992,7 @@
         if (mHealthService == null) {
             return StatsManager.PULL_SKIP;
         }
-        android.hardware.health.V1_0.HealthInfo healthInfo;
+        android.hardware.health.HealthInfo healthInfo;
         try {
             healthInfo = mHealthService.getHealthInfo();
         } catch (RemoteException | IllegalStateException e) {
@@ -4008,13 +4008,13 @@
                 pulledValue = healthInfo.batteryLevel;
                 break;
             case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
-                pulledValue = healthInfo.batteryChargeCounter;
+                pulledValue = healthInfo.batteryChargeCounterUah;
                 break;
             case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
-                pulledValue = healthInfo.batteryFullCharge;
+                pulledValue = healthInfo.batteryFullChargeUah;
                 break;
             case FrameworkStatsLog.BATTERY_VOLTAGE:
-                pulledValue = healthInfo.batteryVoltage;
+                pulledValue = healthInfo.batteryVoltageMillivolts;
                 break;
             case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
                 pulledValue = healthInfo.batteryCycleCount;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index a0f0367..232ea09 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1065,8 +1065,7 @@
 
     @Override
     public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
-            final int backDisposition, final boolean showImeSwitcher,
-            boolean isMultiClientImeEnabled) {
+            final int backDisposition, final boolean showImeSwitcher) {
         enforceStatusBar();
 
         if (SPEW) {
@@ -1083,8 +1082,7 @@
                 if (mBar == null) return;
                 try {
                     mBar.setImeWindowStatus(
-                            displayId, token, vis, backDisposition, showImeSwitcher,
-                            isMultiClientImeEnabled);
+                            displayId, token, vis, backDisposition, showImeSwitcher);
                 } catch (RemoteException ex) { }
             });
         }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 87ce1ce..e9d5ad6 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -317,6 +317,7 @@
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                 userId);
         List<TvInputInfo> inputList = new ArrayList<>();
+        List<ComponentName> hardwareComponents = new ArrayList<>();
         for (ResolveInfo ri : services) {
             ServiceInfo si = ri.serviceInfo;
             if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -327,6 +328,7 @@
 
             ComponentName component = new ComponentName(si.packageName, si.name);
             if (hasHardwarePermission(pm, component)) {
+                hardwareComponents.add(component);
                 ServiceState serviceState = userState.serviceStateMap.get(component);
                 if (serviceState == null) {
                     // New hardware input found. Create a new ServiceState and connect to the
@@ -399,6 +401,15 @@
             }
         }
 
+        // Clean up ServiceState corresponding to the removed hardware inputs
+        Iterator<ServiceState> it = userState.serviceStateMap.values().iterator();
+        while (it.hasNext()) {
+            ServiceState serviceState = it.next();
+            if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) {
+                it.remove();
+            }
+        }
+
         userState.inputMap.clear();
         userState.inputMap = inputMap;
     }
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 3177fac..0436460 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -457,17 +457,18 @@
             if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) {
                 throw new RemoteException("lnbHandle can't be invalid");
             }
-            if (!checkClientExists(clientId)) {
-                throw new RemoteException("Release lnb from unregistered client:" + clientId);
-            }
-            LnbResource lnb = getLnbResource(lnbHandle);
-            if (lnb == null) {
-                throw new RemoteException("Releasing lnb does not exist.");
-            }
-            if (lnb.getOwnerClientId() != clientId) {
-                throw new RemoteException("Client is not the current owner of the releasing lnb.");
-            }
             synchronized (mLock) {
+                if (!checkClientExists(clientId)) {
+                    throw new RemoteException("Release lnb from unregistered client:" + clientId);
+                }
+                LnbResource lnb = getLnbResource(lnbHandle);
+                if (lnb == null) {
+                    throw new RemoteException("Releasing lnb does not exist.");
+                }
+                if (lnb.getOwnerClientId() != clientId) {
+                    throw new RemoteException("Client is not the current owner "
+                            + "of the releasing lnb.");
+                }
                 releaseLnbInternal(lnb);
             }
         }
@@ -869,6 +870,7 @@
 
         frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         ClientProfile requestClient = getClientProfile(request.clientId);
+        // TODO: check if this is really needed
         if (requestClient == null) {
             return false;
         }
@@ -1205,7 +1207,9 @@
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                removeClientProfile(mClientId);
+                if (checkClientExists(mClientId)) {
+                    removeClientProfile(mClientId);
+                }
             }
         }
 
@@ -1246,6 +1250,7 @@
         // Reclaim all the resources of the share owners of the frontend that is used by the current
         // resource reclaimed client.
         ClientProfile profile = getClientProfile(reclaimingClientId);
+        // TODO: check if this check is really needed.
         if (profile == null) {
             return true;
         }
@@ -1553,6 +1558,7 @@
     }
 
     private void clearFrontendAndClientMapping(ClientProfile profile) {
+        // TODO: check if this check is really needed
         if (profile == null) {
             return;
         }
@@ -1573,6 +1579,7 @@
     }
 
     private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+        // TODO: check if this check is really needed. Maybe needed for reclaimResource path.
         if (profile == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e190b1e..26e39c6 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.QUERY_ALL_PACKAGES;
 import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.WallpaperManager.COMMAND_REAPPLY;
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -794,6 +795,7 @@
     private final Context mContext;
     private final WindowManagerInternal mWindowManagerInternal;
     private final IPackageManager mIPackageManager;
+    private final ActivityManager mActivityManager;
     private final MyPackageMonitor mMonitor;
     private final AppOpsManager mAppOpsManager;
 
@@ -942,6 +944,11 @@
          */
         WallpaperColors primaryColors;
 
+        /**
+         * If the wallpaper was set from a foreground app (instead of from a background service).
+         */
+        public boolean fromForegroundApp;
+
         WallpaperConnection connection;
         long lastDiedTime;
         boolean wallpaperUpdating;
@@ -1691,6 +1698,7 @@
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
+        mActivityManager = mContext.getSystemService(ActivityManager.class);
         mMonitor = new MyPackageMonitor();
         mColorsChangedListeners = new SparseArray<>();
 
@@ -2659,6 +2667,9 @@
             }
         }
 
+        final boolean fromForegroundApp = Binder.withCleanCallingIdentity(() ->
+                mActivityManager.getPackageImportance(callingPackage) == IMPORTANCE_FOREGROUND);
+
         synchronized (mLock) {
             if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
             WallpaperData wallpaper;
@@ -2681,6 +2692,7 @@
                     wallpaper.imageWallpaperPending = true;
                     wallpaper.whichPending = which;
                     wallpaper.setComplete = completion;
+                    wallpaper.fromForegroundApp = fromForegroundApp;
                     wallpaper.cropHint.set(cropHint);
                     wallpaper.allowBackup = allowBackup;
                 }
@@ -3063,6 +3075,7 @@
         wallpaper.callbacks.finishBroadcast();
 
         final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+        intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, wallpaper.fromForegroundApp);
         mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 15db790..2f9c138 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -767,10 +767,6 @@
         if (compatStateInfo.mLastLoggedActivity == r) {
             compatStateInfo.mLastLoggedActivity = null;
         }
-        if (compatStateInfo.mVisibleActivities.isEmpty()) {
-            // No need to keep the entry if there are no visible activities.
-            mPackageUidToCompatStateInfo.remove(packageUid);
-        }
     }
 
     /**
@@ -1266,13 +1262,14 @@
      *   activity.
      *   <li>If the current state is NOT_VISIBLE, there is a previously logged state for the
      *   package UID and there are no other visible activities with the same package UID.
-     *   <li>The last logged activity with the same package UID is either {@code activity} or the
-     *   last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
+     *   <li>The last logged activity with the same package UID is either {@code activity} (or an
+     *   activity that has been removed) or the last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
      * </ul>
      *
      * <p>If the current state is NOT_VISIBLE and the previous state which was logged by {@code
-     * activity} wasn't, looks for the first visible activity with the same package UID that has
-     * a letterboxed state, or a non-letterboxed state if there isn't one, and logs that state.
+     * activity} (or an activity that has been removed) wasn't, looks for the first visible activity
+     * with the same package UID that has a letterboxed state, or a non-letterboxed state if
+     * there isn't one, and logs that state.
      *
      * <p>This method assumes that the caller is wrapping the call with a synchronized block so
      * that there won't be a race condition between two activities with the same package.
@@ -1308,14 +1305,14 @@
 
         if (!isVisible && !visibleActivities.isEmpty()) {
             // There is another visible activity for this package UID.
-            if (activity == lastLoggedActivity) {
+            if (lastLoggedActivity == null || activity == lastLoggedActivity) {
                 // Make sure a new visible state is logged if needed.
                 findAppCompatStateToLog(compatStateInfo, packageUid);
             }
             return;
         }
 
-        if (activity != lastLoggedActivity
+        if (lastLoggedActivity != null && activity != lastLoggedActivity
                 && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE
                 && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED) {
             // Another visible activity for this package UID has logged a letterboxed state.
@@ -1329,15 +1326,25 @@
      * Looks for the first visible activity in {@code compatStateInfo} that has a letterboxed
      * state, or a non-letterboxed state if there isn't one, and logs that state for the given
      * {@code packageUid}.
+     *
+     * <p>If there is a visible activity in {@code compatStateInfo} with the same state as the
+     * last logged state for the given {@code packageUid}, changes the last logged activity to
+     * reference the first such activity without actually logging the same state twice.
      */
     private void findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid) {
         final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+        final int lastLoggedState = compatStateInfo.mLastLoggedState;
 
         ActivityRecord activityToLog = null;
         int stateToLog = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
         for (int i = 0; i < visibleActivities.size(); i++) {
             ActivityRecord activity = visibleActivities.get(i);
             int state = activity.getAppCompatState();
+            if (state == lastLoggedState) {
+                // Change last logged activity without logging the same state twice.
+                compatStateInfo.mLastLoggedActivity = activity;
+                return;
+            }
             if (state == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
                 // This shouldn't happen.
                 Slog.w(TAG, "Visible activity with NOT_VISIBLE App Compat state for package UID: "
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index b6552cb..427bbeb 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -27,6 +27,7 @@
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
 import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION;
+import static com.android.server.wm.DisplayRotationProto.IS_FIXED_TO_USER_ROTATION;
 import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
 import static com.android.server.wm.DisplayRotationProto.ROTATION;
 import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
@@ -1527,6 +1528,7 @@
         proto.write(USER_ROTATION, getUserRotation());
         proto.write(FIXED_TO_USER_ROTATION_MODE, mFixedToUserRotation);
         proto.write(LAST_ORIENTATION, mLastOrientation);
+        proto.write(IS_FIXED_TO_USER_ROTATION, isFixedToUserRotation());
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 5145e8e..3de98f1 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -16,10 +16,8 @@
 
 package com.android.server.wm;
 
-import static android.app.UiModeManager.MODE_NIGHT_AUTO;
-import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
-
 import android.annotation.NonNull;
+import android.content.res.Configuration;
 import android.os.Environment;
 import android.os.LocaleList;
 import android.util.AtomicFile;
@@ -308,7 +306,7 @@
         }
 
         boolean isResetNightMode() {
-            return mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM;
+            return mNightMode == Configuration.UI_MODE_NIGHT_UNDEFINED;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b4963c5..b54208d 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -173,10 +172,8 @@
      * to avoid flickering when running PiP animation across different orientations.
      */
     void deferOrientationChangeForEnteringPipFromFullScreenIfNeeded() {
-        final Task topFullscreenTask = mDisplayContent.getDefaultTaskDisplayArea()
-                .getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        final ActivityRecord topFullscreen = topFullscreenTask != null
-                ? topFullscreenTask.topRunningActivity() : null;
+        final ActivityRecord topFullscreen = mDisplayContent.getActivity(
+                a -> a.fillsParent() && !a.getTask().inMultiWindowMode());
         if (topFullscreen == null || topFullscreen.hasFixedRotationTransform()) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4a8f362..35d93f6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1112,7 +1112,11 @@
         if (inMultiWindowMode() || !hasChild()) return false;
         if (intent != null) {
             final int returnHomeFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME;
-            return intent != null && (intent.getFlags() & returnHomeFlags) == returnHomeFlags;
+            final Task task = getDisplayArea() != null ? getDisplayArea().getRootHomeTask() : null;
+            final boolean isLockTaskModeViolation = task != null
+                    && mAtmService.getLockTaskController().isLockTaskModeViolation(task);
+            return (intent.getFlags() & returnHomeFlags) == returnHomeFlags
+                    && !isLockTaskModeViolation;
         }
         final Task bottomTask = getBottomMostTask();
         return bottomTask != this && bottomTask.returnsToHomeRootTask();
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 6bb127a..48a8b1b 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -29,7 +29,10 @@
 
 android_test {
     name: "FrameworksMockingServicesTests",
-    defaults: ["FrameworkMockingServicesTests-jni-defaults"],
+    defaults: [
+        "FrameworkMockingServicesTests-jni-defaults",
+        "modules-utils-testable-device-config-defaults",
+    ],
 
     srcs: [
         "src/**/*.java",
@@ -67,9 +70,7 @@
     ],
 
     jni_libs: [
-        "libdexmakerjvmtiagent",
         "libpsi",
-        "libstaticjvmtiagent",
     ],
 
     certificate: "platform",
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 61cd444..a9099ae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -621,12 +621,8 @@
     }
 
     private void setTareEnabled(boolean enabled) {
-        doReturn(enabled ? 1 : 0).when(
-                () -> Settings.Global.getInt(mContentResolver, Settings.Global.ENABLE_TARE));
-        doReturn(enabled ? 1 : 0).when(
-                () -> Settings.Global.getInt(mContentResolver,
-                        Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE));
-        mService.mConstants.onChange(true);
+        when(mEconomyManagerInternal.isEnabled()).thenReturn(enabled);
+        mService.mConstants.onTareEnabledStateChanged(enabled);
     }
 
     /**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 609768c..766ba72 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -38,10 +38,10 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.modules.utils.testing.TestableDeviceConfig;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
 import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.Before;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index a883293..303f955 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -40,10 +40,10 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.modules.utils.testing.TestableDeviceConfig;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
 import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.After;
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
index ad19a48..d747914c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
@@ -29,7 +29,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index 3aeaa42..41e1237 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -259,8 +259,7 @@
         mAInfo.flags = 0;
 
         allowPackages(TEST_PACKAGE_NAME);
-        // TODO(b/191994709): Fix this assertion once we start checking showWhenLocked
-        assertDoesNotIntercept();
+        assertDoesIntercept();
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
index 007191f..349da03 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -59,7 +59,7 @@
 import com.android.internal.compat.CompatibilityOverrideConfig;
 import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
 import com.android.internal.compat.IPlatformCompat;
-import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
+import com.android.modules.utils.testing.TestableDeviceConfig.TestableDeviceConfigRule;
 
 import org.junit.Before;
 import org.junit.Rule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
index e093f13..9fac0c0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
@@ -34,7 +34,7 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
 
 import org.junit.Before;
 import org.junit.ClassRule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
index f94377f..6a35481 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -36,7 +36,7 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
 
 import org.junit.Before;
 import org.junit.ClassRule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java
deleted file mode 100644
index c0ab70a..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java
+++ /dev/null
@@ -1,163 +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.server.testables;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class MultipleStaticMocksTest {
-    @Rule
-    public StaticMockFixtureRule mStaticMockFixtureRule =
-            new StaticMockFixtureRule(AB::new, CD::new);
-
-    private List<String> mCollected;
-
-    @Test
-    public void testMultipleStaticMocks() throws Exception {
-        mCollected = new ArrayList<>();
-        int n = 0;
-
-        A.a();
-        n = verifyCollected(n, "A.a");
-
-        D.b();
-        n = verifyCollected(n, "D.b");
-
-        C.b();
-        n = verifyCollected(n, "C.b");
-
-        B.a();
-        n = verifyCollected(n, "B.a");
-    }
-
-    private int verifyCollected(int n, String... last) {
-        assertThat(mCollected).hasSize(n + last.length);
-        assertThat(mCollected.subList(n, mCollected.size()))
-                .containsExactlyElementsIn(last).inOrder();
-        return n + last.length;
-    }
-
-    private static class A {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    private static class B {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    private static class C {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    private static class D {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    /**
-     * AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
-     */
-    private class AB implements StaticMockFixture {
-        @Override
-        public StaticMockitoSessionBuilder setUpMockedClasses(
-                StaticMockitoSessionBuilder sessionBuilder) {
-            sessionBuilder.spyStatic(A.class);
-            sessionBuilder.spyStatic(B.class);
-            return sessionBuilder;
-        }
-
-        @Override
-        public void setUpMockBehaviors() {
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("A.a");
-                return null;
-            }).when(A::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("A.b");
-                return null;
-            }).when(A::b);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("B.a");
-                return null;
-            }).when(B::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("B.b");
-                return null;
-            }).when(B::b);
-        }
-
-        @Override
-        public void tearDown() {
-
-        }
-    }
-
-    /**
-     * AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
-     */
-    private class CD implements StaticMockFixture {
-        @Override
-        public StaticMockitoSessionBuilder setUpMockedClasses(
-                StaticMockitoSessionBuilder sessionBuilder) {
-            sessionBuilder.spyStatic(C.class);
-            sessionBuilder.spyStatic(D.class);
-            return sessionBuilder;
-        }
-
-        @Override
-        public void setUpMockBehaviors() {
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("C.a");
-                return null;
-            }).when(C::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("C.b");
-                return null;
-            }).when(C::b);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("D.a");
-                return null;
-            }).when(D::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("D.b");
-                return null;
-            }).when(D::b);
-        }
-
-        @Override
-        public void tearDown() {
-
-        }
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java
deleted file mode 100644
index 0303fe1..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java
+++ /dev/null
@@ -1,56 +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.server.testables;
-
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-/**
- * Provides support for a set of static mocks for use within a single shared
- * {@link StaticMockitoSession}.
- */
-public interface StaticMockFixture {
-    /**
-     * Adds any required mock or spy classes managed by this {@link StaticMockFixture} to the
-     * {@link StaticMockitoSessionBuilder} provided.
-     *
-     * Call this to set up the classes that this expects to be mocked, by adding them to the
-     * {@link StaticMockitoSessionBuilder} using
-     * {@link StaticMockitoSessionBuilder#mockStatic(Class)},
-     * {@link StaticMockitoSessionBuilder#spyStatic(Class)} or similar as appropriate.
-     *
-     * @param sessionBuilder the {@link StaticMockitoSessionBuilder} to which the classes should be
-     *                       added to mock, spy, or otherwise as required
-     * @return sessionBuilder, to allow for fluent programming
-     */
-    StaticMockitoSessionBuilder setUpMockedClasses(StaticMockitoSessionBuilder sessionBuilder);
-
-    /**
-     * Configures the behaviours of any mock or spy classes managed by this
-     * {@link StaticMockFixture}.
-     *
-     * Call this after {@link StaticMockitoSessionBuilder#startMocking()} has been called.
-     * This sets up any default behaviors for the mocks, spys, etc.
-     */
-    void setUpMockBehaviors();
-
-    /**
-     * Tear everything down.
-     */
-    void tearDown();
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
deleted file mode 100644
index 3566aee..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
+++ /dev/null
@@ -1,141 +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.server.testables;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.rules.TestRule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Supplier;
-
-/**
- * <p>StaticMockFixtureRule is a {@link TestRule} that wraps one or more {@link StaticMockFixture}s
- * to set them up and tear it down automatically. This works well when you have no other static
- * mocks than the ones supported by their respective {@link StaticMockFixture}s.</p>
- *
- * <p>StaticMockFixtureRule should be defined as a rule on your test so it can clean up after
- * itself. Like the following:</p>
- * <pre class="prettyprint">
-*  public final StaticMockFixture mStaticMockFixtures = ...;
- * &#064;Rule
- * public final StaticMockFixtureRule mStaticMockFixtureRule =
- *     new StaticMockFixtureRule(mStaticMockFixtures);
- * </pre>
- */
-public class StaticMockFixtureRule implements TestRule {
-    private StaticMockitoSession mMockitoSession;
-    private StaticMockFixture[] mStaticMockFixtures;
-    private Supplier<? extends StaticMockFixture>[] mSupplier;
-
-    /**
-     * Constructs a StaticMockFixtureRule that always uses the same {@link StaticMockFixture}
-     * instance(s).
-     *
-     * @param staticMockFixtures the {@link StaticMockFixture}(s) to use.
-     */
-    public StaticMockFixtureRule(StaticMockFixture... staticMockFixtures) {
-        mStaticMockFixtures = staticMockFixtures;
-        mSupplier = null;
-    }
-
-    /**
-     * Constructs a StaticMockFixtureRule that retrieves a new {@link StaticMockFixture} instance
-     * from one or more {@link Supplier<? extends   StaticMockFixture  >}s for each test invocation.
-     *
-     * @param supplier the {@link Supplier<? extends   StaticMockFixture  >}(s) that will supply the
-     * {@link StaticMockFixture}(s).
-     */
-    @SafeVarargs
-    public StaticMockFixtureRule(Supplier<? extends StaticMockFixture>... supplier) {
-        mStaticMockFixtures = null;
-        mSupplier = supplier;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        StaticMockitoSessionBuilder sessionBuilder = getSessionBuilder();
-
-        if (mSupplier != null) {
-            mStaticMockFixtures = new StaticMockFixture[mSupplier.length];
-            for (int i = 0; i < mSupplier.length; i++) {
-                mStaticMockFixtures[i] = mSupplier[i].get();
-            }
-        }
-
-        for (int i = 0; i < mStaticMockFixtures.length; i++) {
-            sessionBuilder = mStaticMockFixtures[i].setUpMockedClasses(sessionBuilder);
-        }
-
-        mMockitoSession = sessionBuilder.startMocking();
-
-        for (int i = 0; i < mStaticMockFixtures.length; i++) {
-            mStaticMockFixtures[i].setUpMockBehaviors();
-        }
-
-        return new TestWatcher() {
-            @Override
-            protected void succeeded(Description description) {
-                tearDown(null);
-            }
-
-            @Override
-            protected void skipped(AssumptionViolatedException e, Description description) {
-                tearDown(e);
-            }
-
-            @Override
-            protected void failed(Throwable e, Description description) {
-                tearDown(e);
-            }
-        }.apply(base, description);
-    }
-
-    /**
-     * This allows overriding the creation of the builder for a new {@link StaticMockitoSession}.
-     * Mainly for testing, but also useful if you have other requirements for the session.
-     *
-     * @return a new {@link StaticMockitoSessionBuilder}.
-     */
-    public StaticMockitoSessionBuilder getSessionBuilder() {
-        return mockitoSession().strictness(Strictness.LENIENT);
-    }
-
-    private void tearDown(Throwable e) {
-        mMockitoSession.finishMocking(e);
-
-        for (int i = mStaticMockFixtures.length - 1; i >= 0; i--) {
-            mStaticMockFixtures[i].tearDown();
-            if (mSupplier != null) {
-                mStaticMockFixtures[i] = null;
-            }
-        }
-
-        if (mSupplier != null) {
-            mStaticMockFixtures = null;
-        }
-
-        mMockitoSession = null;
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
deleted file mode 100644
index 8e0ccf0..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
+++ /dev/null
@@ -1,258 +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.server.testables;
-
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.quality.Strictness.LENIENT;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.After;
-import org.junit.AssumptionViolatedException;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoSession;
-
-import java.util.function.Supplier;
-
-/** Tests that StaticMockFixture manages fixtures and suppliers correctly. */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class StaticMockFixtureRuleTest {
-    private MockitoSession mMockitoSession;
-
-    @Mock private StaticMockitoSessionBuilder mSessionBuilder;
-    @Mock private StaticMockitoSession mSession;
-    @Mock private StaticMockFixture mA1;
-    @Mock private StaticMockFixture mB1;
-    @Mock private StaticMockFixture mA2;
-    @Mock private StaticMockFixture mB2;
-    @Mock private Supplier<StaticMockFixture> mSupplyA;
-    @Mock private Supplier<StaticMockFixture> mSupplyB;
-    @Mock private Statement mStatement;
-    @Mock private Statement mSkipStatement;
-    @Mock private Statement mThrowStatement;
-    @Mock private Description mDescription;
-
-    @Before
-    public void setUp() throws Throwable {
-        mMockitoSession = Mockito.mockitoSession()
-                .strictness(LENIENT)
-                .initMocks(this)
-                .startMocking();
-        prepareMockBehaviours();
-    }
-
-    @After
-    public void tearDown() {
-        mMockitoSession.finishMocking();
-    }
-
-    private void prepareFixtureMocks(StaticMockFixture... mocks) {
-        for (StaticMockFixture mock : mocks) {
-            when(mock.setUpMockedClasses(any())).thenAnswer(
-                    invocation -> invocation.getArgument(0));
-            doNothing().when(mock).setUpMockBehaviors();
-        }
-    }
-
-    private void prepareMockBehaviours() throws Throwable {
-        when(mSessionBuilder.startMocking()).thenReturn(mSession);
-        when(mSupplyA.get()).thenReturn(mA1, mA2);
-        when(mSupplyB.get()).thenReturn(mB1, mB2);
-        prepareFixtureMocks(mA1, mA2, mB1, mB2);
-        when(mA1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
-        doNothing().when(mA1).setUpMockBehaviors();
-        when(mB1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
-        doNothing().when(mB1).setUpMockBehaviors();
-        doNothing().when(mStatement).evaluate();
-        doThrow(new AssumptionViolatedException("bad assumption, test should be skipped"))
-                .when(mSkipStatement).evaluate();
-        doThrow(new IllegalArgumentException("bad argument, test should be failed"))
-                .when(mThrowStatement).evaluate();
-        doNothing().when(mA1).tearDown();
-        doNothing().when(mB1).tearDown();
-    }
-
-    private InOrder mocksInOrder()  {
-        return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB, mA1, mA2, mB1, mB2,
-                mStatement, mSkipStatement, mThrowStatement, mDescription);
-    }
-
-    private void verifyNoMoreImportantMockInteractions()  {
-        verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement,
-                mSkipStatement, mThrowStatement);
-    }
-
-    @Test
-    public void testRuleWorksWithExplicitFixtures() throws Throwable {
-        InOrder inOrder = mocksInOrder();
-
-        StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
-            @Override public StaticMockitoSessionBuilder getSessionBuilder() {
-                return mSessionBuilder;
-            }
-        };
-        Statement runMe = rule.apply(mStatement, mDescription);
-
-        inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mA1).setUpMockBehaviors();
-        inOrder.verify(mB1).setUpMockBehaviors();
-
-        runMe.evaluate();
-
-        inOrder.verify(mStatement).evaluate();
-        // note: tearDown in reverse order
-        inOrder.verify(mB1).tearDown();
-        inOrder.verify(mA1).tearDown();
-
-        // Round two: use the same fixtures again.
-        rule.apply(mStatement, mDescription).evaluate();
-
-        inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mA1).setUpMockBehaviors();
-        inOrder.verify(mB1).setUpMockBehaviors();
-        inOrder.verify(mStatement).evaluate();
-        // note: tearDown in reverse order
-        inOrder.verify(mB1).tearDown();
-        inOrder.verify(mA1).tearDown();
-
-        verifyNoMoreImportantMockInteractions();
-    }
-
-    @Test
-    public void testRuleWorksWithFixtureSuppliers() throws Throwable {
-        InOrder inOrder = mocksInOrder();
-
-        StaticMockFixtureRule rule = new StaticMockFixtureRule(mSupplyA, mSupplyB) {
-            @Override public StaticMockitoSessionBuilder getSessionBuilder() {
-                return mSessionBuilder;
-            }
-        };
-        Statement runMe = rule.apply(mStatement, mDescription);
-
-        inOrder.verify(mSupplyA).get();
-        inOrder.verify(mSupplyB).get();
-        inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mA1).setUpMockBehaviors();
-        inOrder.verify(mB1).setUpMockBehaviors();
-
-        runMe.evaluate();
-
-        inOrder.verify(mStatement).evaluate();
-        // note: tearDown in reverse order
-        inOrder.verify(mB1).tearDown();
-        inOrder.verify(mA1).tearDown();
-
-        // Round two: use the same suppliers again to retrieve different fixtures: mA2 and mB2
-        rule.apply(mStatement, mDescription).evaluate();
-
-        inOrder.verify(mSupplyA).get();
-        inOrder.verify(mSupplyB).get();
-        inOrder.verify(mA2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mB2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mA2).setUpMockBehaviors();
-        inOrder.verify(mB2).setUpMockBehaviors();
-        inOrder.verify(mStatement).evaluate();
-        // note: tearDown in reverse order
-        inOrder.verify(mB2).tearDown();
-        inOrder.verify(mA2).tearDown();
-
-        verifyNoMoreImportantMockInteractions();
-    }
-
-    @Test
-    public void testTearDownOnSkippedTests() throws Throwable {
-        InOrder inOrder = mocksInOrder();
-
-        StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
-            @Override public StaticMockitoSessionBuilder getSessionBuilder() {
-                return mSessionBuilder;
-            }
-        };
-        Statement skipStatement = rule.apply(mSkipStatement, mDescription);
-
-        inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mA1).setUpMockBehaviors();
-        inOrder.verify(mB1).setUpMockBehaviors();
-
-        try {
-            skipStatement.evaluate();
-            fail("AssumptionViolatedException should have been thrown");
-        } catch (AssumptionViolatedException e) {
-            // expected
-        }
-
-        inOrder.verify(mSkipStatement).evaluate();
-        // note: tearDown in reverse order
-        inOrder.verify(mB1).tearDown();
-        inOrder.verify(mA1).tearDown();
-
-        verifyNoMoreImportantMockInteractions();
-    }
-
-    @Test
-    public void testTearDownOnFailedTests() throws Throwable {
-        InOrder inOrder = mocksInOrder();
-
-        StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
-            @Override public StaticMockitoSessionBuilder getSessionBuilder() {
-                return mSessionBuilder;
-            }
-        };
-        Statement failStatement = rule.apply(mThrowStatement, mDescription);
-
-        inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
-        inOrder.verify(mA1).setUpMockBehaviors();
-        inOrder.verify(mB1).setUpMockBehaviors();
-
-        try {
-            failStatement.evaluate();
-            fail("IllegalArgumentException should have been thrown");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-
-        inOrder.verify(mThrowStatement).evaluate();
-        // note: tearDown in reverse order
-        inOrder.verify(mB1).tearDown();
-        inOrder.verify(mA1).tearDown();
-
-        verifyNoMoreImportantMockInteractions();
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
deleted file mode 100644
index 60a7f78..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ /dev/null
@@ -1,284 +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.server.testables;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.when;
-
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
-import android.util.ArrayMap;
-import android.util.Pair;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.rules.TestRule;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
-
-/**
- * TestableDeviceConfig is a {@link StaticMockFixture} that uses ExtendedMockito to replace the real
- * implementation of DeviceConfig with essentially a local HashMap in the callers process. This
- * allows for unit testing that do not modify the real DeviceConfig on the device at all.
- */
-public final class TestableDeviceConfig implements StaticMockFixture {
-
-    private Map<DeviceConfig.OnPropertiesChangedListener, Pair<String, Executor>>
-            mOnPropertiesChangedListenerMap = new HashMap<>();
-    private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>();
-
-    /**
-     * Clears out all local overrides.
-     */
-    public void clearDeviceConfig() {
-        mKeyValueMap.clear();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public StaticMockitoSessionBuilder setUpMockedClasses(
-            StaticMockitoSessionBuilder sessionBuilder) {
-        sessionBuilder.spyStatic(DeviceConfig.class);
-        return sessionBuilder;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setUpMockBehaviors() {
-        doAnswer((Answer<Void>) invocationOnMock -> {
-            String namespace = invocationOnMock.getArgument(0);
-            Executor executor = invocationOnMock.getArgument(1);
-            DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener =
-                    invocationOnMock.getArgument(2);
-            mOnPropertiesChangedListenerMap.put(
-                    onPropertiesChangedListener, new Pair<>(namespace, executor));
-            return null;
-        }).when(() -> DeviceConfig.addOnPropertiesChangedListener(
-                anyString(), any(Executor.class),
-                any(DeviceConfig.OnPropertiesChangedListener.class)));
-
-        doAnswer((Answer<Boolean>) invocationOnMock -> {
-                    String namespace = invocationOnMock.getArgument(0);
-                    String name = invocationOnMock.getArgument(1);
-                    String value = invocationOnMock.getArgument(2);
-                    mKeyValueMap.put(getKey(namespace, name), value);
-                    invokeListeners(namespace, getProperties(namespace, name, value));
-                    return true;
-                }
-        ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
-
-        doAnswer((Answer<Boolean>) invocationOnMock -> {
-                    String namespace = invocationOnMock.getArgument(0);
-                    String name = invocationOnMock.getArgument(1);
-                    mKeyValueMap.remove(getKey(namespace, name));
-                    invokeListeners(namespace, getProperties(namespace, name, null));
-                    return true;
-                }
-        ).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
-
-        doAnswer((Answer<Boolean>) invocationOnMock -> {
-                    Properties properties = invocationOnMock.getArgument(0);
-                    String namespace = properties.getNamespace();
-                    Map<String, String> keyValues = new ArrayMap<>();
-                    for (String name : properties.getKeyset()) {
-                        String value = properties.getString(name, /* defaultValue= */ "");
-                        mKeyValueMap.put(getKey(namespace, name), value);
-                        keyValues.put(name.toLowerCase(), value);
-                    }
-                    invokeListeners(namespace, getProperties(namespace, keyValues));
-                    return true;
-                }
-        ).when(() -> DeviceConfig.setProperties(any(Properties.class)));
-
-        doAnswer((Answer<String>) invocationOnMock -> {
-            String namespace = invocationOnMock.getArgument(0);
-            String name = invocationOnMock.getArgument(1);
-            return mKeyValueMap.get(getKey(namespace, name));
-        }).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
-
-        doAnswer((Answer<Properties>) invocationOnMock -> {
-            String namespace = invocationOnMock.getArgument(0);
-            final int varargStartIdx = 1;
-            Map<String, String> keyValues = new ArrayMap<>();
-            if (invocationOnMock.getArguments().length == varargStartIdx) {
-                mKeyValueMap.entrySet().forEach(entry -> {
-                    Pair<String, String> nameSpaceAndName = getNameSpaceAndName(entry.getKey());
-                    if (!nameSpaceAndName.first.equals(namespace)) {
-                        return;
-                    }
-                    keyValues.put(nameSpaceAndName.second.toLowerCase(), entry.getValue());
-                });
-            } else {
-                for (int i = varargStartIdx; i < invocationOnMock.getArguments().length; ++i) {
-                    String name = invocationOnMock.getArgument(i);
-                    keyValues.put(name.toLowerCase(), mKeyValueMap.get(getKey(namespace, name)));
-                }
-            }
-            return getProperties(namespace, keyValues);
-        }).when(() -> DeviceConfig.getProperties(anyString(), ArgumentMatchers.<String>any()));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void tearDown() {
-        clearDeviceConfig();
-        mOnPropertiesChangedListenerMap.clear();
-    }
-
-    private static String getKey(String namespace, String name) {
-        return namespace + "/" + name;
-    }
-
-    private Pair<String, String> getNameSpaceAndName(String key) {
-        final String[] values = key.split("/");
-        return Pair.create(values[0], values[1]);
-    }
-
-    private void invokeListeners(String namespace, Properties properties) {
-        for (DeviceConfig.OnPropertiesChangedListener listener :
-                mOnPropertiesChangedListenerMap.keySet()) {
-            if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
-                mOnPropertiesChangedListenerMap.get(listener).second.execute(
-                        () -> listener.onPropertiesChanged(properties));
-            }
-        }
-    }
-
-    private Properties getProperties(String namespace, String name, String value) {
-        return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value));
-    }
-
-    private Properties getProperties(String namespace, Map<String, String> keyValues) {
-        Properties properties = Mockito.mock(Properties.class);
-        when(properties.getNamespace()).thenReturn(namespace);
-        when(properties.getKeyset()).thenReturn(keyValues.keySet());
-        when(properties.getBoolean(anyString(), anyBoolean())).thenAnswer(
-                invocation -> {
-                    String key = invocation.getArgument(0);
-                    boolean defaultValue = invocation.getArgument(1);
-                    final String value = keyValues.get(key.toLowerCase());
-                    if (value != null) {
-                        return Boolean.parseBoolean(value);
-                    } else {
-                        return defaultValue;
-                    }
-                }
-        );
-        when(properties.getFloat(anyString(), anyFloat())).thenAnswer(
-                invocation -> {
-                    String key = invocation.getArgument(0);
-                    float defaultValue = invocation.getArgument(1);
-                    final String value = keyValues.get(key.toLowerCase());
-                    if (value != null) {
-                        try {
-                            return Float.parseFloat(value);
-                        } catch (NumberFormatException e) {
-                            return defaultValue;
-                        }
-                    } else {
-                        return defaultValue;
-                    }
-                }
-        );
-        when(properties.getInt(anyString(), anyInt())).thenAnswer(
-                invocation -> {
-                    String key = invocation.getArgument(0);
-                    int defaultValue = invocation.getArgument(1);
-                    final String value = keyValues.get(key.toLowerCase());
-                    if (value != null) {
-                        try {
-                            return Integer.parseInt(value);
-                        } catch (NumberFormatException e) {
-                            return defaultValue;
-                        }
-                    } else {
-                        return defaultValue;
-                    }
-                }
-        );
-        when(properties.getLong(anyString(), anyLong())).thenAnswer(
-                invocation -> {
-                    String key = invocation.getArgument(0);
-                    long defaultValue = invocation.getArgument(1);
-                    final String value = keyValues.get(key.toLowerCase());
-                    if (value != null) {
-                        try {
-                            return Long.parseLong(value);
-                        } catch (NumberFormatException e) {
-                            return defaultValue;
-                        }
-                    } else {
-                        return defaultValue;
-                    }
-                }
-        );
-        when(properties.getString(anyString(), nullable(String.class))).thenAnswer(
-                invocation -> {
-                    String key = invocation.getArgument(0);
-                    String defaultValue = invocation.getArgument(1);
-                    final String value = keyValues.get(key.toLowerCase());
-                    if (value != null) {
-                        return value;
-                    } else {
-                        return defaultValue;
-                    }
-                }
-        );
-
-        return properties;
-    }
-
-    /**
-     * <p>TestableDeviceConfigRule is a {@link TestRule} that wraps a {@link TestableDeviceConfig}
-     * to set it up and tear it down automatically. This works well when you have no other static
-     * mocks.</p>
-     *
-     * <p>TestableDeviceConfigRule should be defined as a rule on your test so it can clean up after
-     * itself. Like the following:</p>
-     * <pre class="prettyprint">
-     * &#064;Rule
-     * public final TestableDeviceConfigRule mTestableDeviceConfigRule =
-     *     new TestableDeviceConfigRule();
-     * </pre>
-     */
-    public static class TestableDeviceConfigRule extends StaticMockFixtureRule {
-        public TestableDeviceConfigRule() {
-            super(TestableDeviceConfig::new);
-        }
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java
deleted file mode 100644
index 9616d13..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java
+++ /dev/null
@@ -1,172 +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.server.testables;
-
-import android.provider.DeviceConfig;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TestableDeviceConfigAndOtherStaticMocksTest {
-    @Rule
-    public StaticMockFixtureRule mStaticMockFixtureRule =
-            new StaticMockFixtureRule(TestableDeviceConfig::new, AB::new, CD::new);
-
-    private List<String> mCollected;
-
-    @Test
-    public void testDeviceConfigAndOtherStaticMocks() throws Exception {
-        mCollected = new ArrayList<>();
-        int n = 0;
-
-        String namespace = "foo";
-        String flag = "bar";
-        String flagValue = "new value";
-
-        Assert.assertNull(DeviceConfig.getProperty(namespace, flag));
-
-        A.a();
-        verifyCollected(++n, "A.a");
-
-        DeviceConfig.setProperty(namespace, flag, flagValue, false);
-
-        D.b();
-        verifyCollected(++n, "D.b");
-
-        Assert.assertEquals(flagValue, DeviceConfig.getProperty(namespace, flag));
-
-        C.b();
-        verifyCollected(++n, "C.b");
-
-        B.a();
-        verifyCollected(++n, "B.a");
-    }
-
-    private void verifyCollected(int n, String last) {
-        Assert.assertEquals(n, mCollected.size());
-        Assert.assertEquals(last, mCollected.get(n - 1));
-    }
-
-    private static class A {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    private static class B {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    private static class C {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    private static class D {
-        /* package */ static void a() {}
-        /* package */ static void b() {}
-    }
-
-    /**
-     * AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
-     */
-    private class AB implements StaticMockFixture {
-        @Override
-        public StaticMockitoSessionBuilder setUpMockedClasses(
-                StaticMockitoSessionBuilder sessionBuilder) {
-            sessionBuilder.spyStatic(A.class);
-            sessionBuilder.spyStatic(B.class);
-            return sessionBuilder;
-        }
-
-        @Override
-        public void setUpMockBehaviors() {
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("A.a");
-                return null;
-            }).when(A::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("A.b");
-                return null;
-            }).when(A::b);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("B.a");
-                return null;
-            }).when(B::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("B.b");
-                return null;
-            }).when(B::b);
-        }
-
-        @Override
-        public void tearDown() {
-
-        }
-    }
-
-    /**
-     * AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
-     */
-    private class CD implements StaticMockFixture {
-        @Override
-        public StaticMockitoSessionBuilder setUpMockedClasses(
-                StaticMockitoSessionBuilder sessionBuilder) {
-            sessionBuilder.spyStatic(C.class);
-            sessionBuilder.spyStatic(D.class);
-            return sessionBuilder;
-        }
-
-        @Override
-        public void setUpMockBehaviors() {
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("C.a");
-                return null;
-            }).when(C::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("C.b");
-                return null;
-            }).when(C::b);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("D.a");
-                return null;
-            }).when(D::a);
-            ExtendedMockito.doAnswer(invocation -> {
-                mCollected.add("D.b");
-                return null;
-            }).when(D::b);
-        }
-
-        @Override
-        public void tearDown() {
-
-        }
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
deleted file mode 100644
index f9f4387..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
+++ /dev/null
@@ -1,229 +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.server.testables;
-
-import static android.provider.DeviceConfig.OnPropertiesChangedListener;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.ActivityThread;
-import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.BadConfigException;
-import android.provider.DeviceConfig.Properties;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/** Tests that ensure appropriate settings are backed up. */
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TestableDeviceConfigTest {
-    private static final String sNamespace = "namespace1";
-    private static final String sKey = "key1";
-    private static final String sValue = "value1";
-    private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
-
-    @Rule
-    public TestableDeviceConfig.TestableDeviceConfigRule
-            mTestableDeviceConfig = new TestableDeviceConfig.TestableDeviceConfigRule();
-
-    @Test
-    public void getProperty_empty() {
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertThat(result).isNull();
-    }
-
-    @Test
-    public void setAndGetProperty_sameNamespace() {
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertThat(result).isEqualTo(sValue);
-    }
-
-    @Test
-    public void setAndGetProperty_differentNamespace() {
-        String newNamespace = "namespace2";
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        String result = DeviceConfig.getProperty(newNamespace, sKey);
-        assertThat(result).isNull();
-    }
-
-    @Test
-    public void setAndGetProperty_multipleNamespaces() {
-        String newNamespace = "namespace2";
-        String newValue = "value2";
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertThat(result).isEqualTo(sValue);
-        result = DeviceConfig.getProperty(newNamespace, sKey);
-        assertThat(result).isEqualTo(newValue);
-    }
-
-    @Test
-    public void setAndGetProperty_overrideValue() {
-        String newValue = "value2";
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
-        String result = DeviceConfig.getProperty(sNamespace, sKey);
-        assertThat(result).isEqualTo(newValue);
-    }
-
-    @Test
-    public void setProperties() throws BadConfigException {
-        String newKey = "key2";
-        String newValue = "value2";
-        DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
-                sValue).setString(newKey, newValue).build());
-        assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
-        assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
-    }
-
-    @Test
-    public void deleteProperty() {
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
-        DeviceConfig.deleteProperty(sNamespace, sKey);
-        assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
-        String newNamespace = "namespace2";
-        String newValue = "value2";
-        DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
-        assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
-        DeviceConfig.deleteProperty(newNamespace, sKey);
-        assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
-    }
-
-    @Test
-    public void getProperties_empty() {
-        String newKey = "key2";
-        String newValue = "value2";
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        Properties properties = DeviceConfig.getProperties(sNamespace);
-        assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
-        assertThat(properties.getString(newKey, null)).isNull();
-
-        DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
-        properties = DeviceConfig.getProperties(sNamespace);
-        assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
-        assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
-
-    }
-
-    @Test
-    public void getProperties() {
-        Properties properties = DeviceConfig.getProperties(sNamespace, sKey);
-        assertThat(properties.getString(sKey, null)).isNull();
-
-        DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-        properties = DeviceConfig.getProperties(sNamespace, sKey);
-        assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
-
-        String newKey = "key2";
-        String newValue = "value2";
-        DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
-        properties = DeviceConfig.getProperties(sNamespace, sKey, newKey);
-        assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
-        assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
-
-        String unsetKey = "key3";
-        properties = DeviceConfig.getProperties(sNamespace, newKey, unsetKey);
-        assertThat(properties.getKeyset()).containsExactly(newKey, unsetKey);
-        assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
-        assertThat(properties.getString(unsetKey, null)).isNull();
-    }
-
-    @Test
-    public void testListener_setProperty() throws InterruptedException {
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-
-        OnPropertiesChangedListener changeListener = (properties) -> {
-            assertThat(properties.getNamespace()).isEqualTo(sNamespace);
-            assertThat(properties.getKeyset()).containsExactly(sKey);
-            assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
-            assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
-            countDownLatch.countDown();
-        };
-        try {
-            DeviceConfig.addOnPropertiesChangedListener(sNamespace,
-                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
-            DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
-            assertThat(countDownLatch.await(
-                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
-        } finally {
-            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
-        }
-    }
-
-    @Test
-    public void testListener_setProperties() throws BadConfigException, InterruptedException {
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-        String newKey = "key2";
-        String newValue = "value2";
-
-        OnPropertiesChangedListener changeListener = (properties) -> {
-            assertThat(properties.getNamespace()).isEqualTo(sNamespace);
-            assertThat(properties.getKeyset()).containsExactly(sKey, newKey);
-            assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
-            assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue);
-            assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
-            countDownLatch.countDown();
-        };
-        try {
-            DeviceConfig.addOnPropertiesChangedListener(sNamespace,
-                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
-            DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
-                    sValue).setString(newKey, newValue).build());
-            assertThat(countDownLatch.await(
-                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
-        } finally {
-            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
-        }
-    }
-
-    @Test
-    public void testListener_deleteProperty() throws InterruptedException {
-        CountDownLatch countDownLatch = new CountDownLatch(1);
-
-        OnPropertiesChangedListener changeListener = (properties) -> {
-            assertThat(properties.getNamespace()).isEqualTo(sNamespace);
-            assertThat(properties.getKeyset()).containsExactly(sKey);
-            assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
-            assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
-            countDownLatch.countDown();
-        };
-        try {
-            DeviceConfig.addOnPropertiesChangedListener(sNamespace,
-                    ActivityThread.currentApplication().getMainExecutor(), changeListener);
-            DeviceConfig.deleteProperty(sNamespace, sKey);
-            assertThat(countDownLatch.await(
-                    WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
-        } finally {
-            DeviceConfig.removeOnPropertiesChangedListener(changeListener);
-        }
-    }
-}
-
-
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 da881c4..15ba358 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
@@ -19,6 +19,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -169,13 +170,14 @@
     }
 
     @Test
-    public void enable_hasConnection_enableWindowMagnification() throws RemoteException {
+    public void enableWithAnimation_hasConnection_enableWindowMagnification()
+            throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
 
         mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
 
-        verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f,
-                200f, 300f, null);
+        verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+                eq(200f), eq(300f), notNull());
     }
 
     @Test
@@ -199,7 +201,8 @@
 
         mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
 
-        verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+        verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+                notNull());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
index 54e75aa..bf7c587 100644
--- a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
@@ -29,6 +29,8 @@
 import com.android.server.criticalevents.nano.CriticalEventProto;
 import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
 import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
 import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
 
 import org.junit.Before;
@@ -255,24 +257,39 @@
     }
 
     @Test
-    public void privacyRedaction_anr() {
+    public void logJavaCrash() {
         mCriticalEventLog.incTimeSeconds(1);
-        mCriticalEventLog.logAnr("Subject 1", ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
-                SYSTEM_SERVER_UID, 0);
-        mCriticalEventLog.incTimeSeconds(1);
-        mCriticalEventLog.logAnr("Subject 2", ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
-                SYSTEM_APP_UID, 1);
-        mCriticalEventLog.incTimeSeconds(1);
-        mCriticalEventLog.logAnr("Subject 3", ServerProtoEnums.DATA_APP, "com.foo",
-                DATA_APP_UID, 2);
-        mCriticalEventLog.incTimeSeconds(1);
-        mCriticalEventLog.logAnr("Subject 4", ServerProtoEnums.DATA_APP, "com.foo",
-                DATA_APP_UID_2, 3);
-        mCriticalEventLog.incTimeSeconds(1);
-        mCriticalEventLog.logAnr("Subject 5", ServerProtoEnums.DATA_APP, "com.bar",
-                DATA_APP_UID_3, 4);
+        mCriticalEventLog.logJavaCrash("com.android.MyClass", ServerProtoEnums.SYSTEM_APP,
+                "AID_RADIO", SYSTEM_APP_UID, 1);
         mCriticalEventLog.incTimeSeconds(1);
 
+        CriticalEventLogProto logProto = getLogOutput();
+
+        assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+        assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+                javaCrash(START_TIME_MS + 1000, "com.android.MyClass", ServerProtoEnums.SYSTEM_APP,
+                        "AID_RADIO", SYSTEM_APP_UID, 1)
+        });
+    }
+
+    @Test
+    public void logNativeCrash() {
+        mCriticalEventLog.incTimeSeconds(1);
+        mCriticalEventLog.logNativeCrash(ServerProtoEnums.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID,
+                1);
+        mCriticalEventLog.incTimeSeconds(1);
+
+        CriticalEventLogProto logProto = getLogOutput();
+
+        assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+        assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+                nativeCrash(START_TIME_MS + 1000, ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+                        SYSTEM_APP_UID, 1)
+        });
+    }
+
+    @Test
+    public void privacyRedaction_anr() {
         CriticalEventProto systemServerAnr = anr(START_TIME_MS + 1000, "Subject 1",
                 CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM", SYSTEM_SERVER_UID, 0);
         CriticalEventProto systemAppAnr = anr(START_TIME_MS + 2000, "Subject 2",
@@ -289,6 +306,8 @@
         CriticalEventProto barAppAnrRedacted = anr(START_TIME_MS + 5000, "",
                 CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
 
+        addToLog(systemServerAnr, systemAppAnr, fooAppAnr, fooAppAnrUid2, barAppAnr);
+
         assertProtoArrayEquals(
                 getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
                 new CriticalEventProto[]{
@@ -325,9 +344,123 @@
     }
 
     @Test
-    public void privacyRedaction_anr_doesNotMutateLogState() {
-        mCriticalEventLog.logAnr("Subject", ServerProtoEnums.DATA_APP, "com.foo",
+    public void privacyRedaction_javaCrash() {
+        CriticalEventProto systemServerCrash = javaCrash(START_TIME_MS + 1000, "Exception class 1",
+                CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM",
+                SYSTEM_SERVER_UID, 0);
+        CriticalEventProto systemAppCrash = javaCrash(START_TIME_MS + 2000, "Exception class 2",
+                CriticalEventProto.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID, 1);
+        CriticalEventProto fooAppCrash = javaCrash(START_TIME_MS + 3000, "Exception class 3",
+                CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+        CriticalEventProto fooAppCrashUid2 = javaCrash(START_TIME_MS + 4000, "Exception class 4",
+                CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+        CriticalEventProto fooAppCrashUid2Redacted = javaCrash(START_TIME_MS + 4000, "",
+                CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+        CriticalEventProto barAppCrash = javaCrash(START_TIME_MS + 5000, "Exception class 5",
+                CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+        CriticalEventProto barAppCrashRedacted = javaCrash(START_TIME_MS + 5000, "",
+                CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+        addToLog(systemServerCrash, systemAppCrash, fooAppCrash, fooAppCrashUid2, barAppCrash);
+
+        assertProtoArrayEquals(
+                getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+                new CriticalEventProto[]{
+                        systemServerCrash,
+                        systemAppCrash,
+                        fooAppCrash,
+                        // Redacted since the trace file and crash are for different uids.
+                        fooAppCrashUid2Redacted,
+                        // Redacted since the trace file and crash are for different data apps.
+                        barAppCrashRedacted
+                });
+
+        assertProtoArrayEquals(
+                getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+                        SYSTEM_SERVER_UID).events,
+                new CriticalEventProto[]{
+                        systemServerCrash,
+                        systemAppCrash,
+                        fooAppCrash,
+                        fooAppCrashUid2,
+                        barAppCrash
+                });
+
+        assertProtoArrayEquals(
+                getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+                        SYSTEM_APP_UID).events,
+                new CriticalEventProto[]{
+                        systemServerCrash,
+                        systemAppCrash,
+                        fooAppCrash,
+                        fooAppCrashUid2,
+                        barAppCrash
+                });
+    }
+
+    @Test
+    public void privacyRedaction_nativeCrash() {
+        CriticalEventProto systemServerCrash = nativeCrash(START_TIME_MS + 1000,
+                CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM",
+                SYSTEM_SERVER_UID, 0);
+        CriticalEventProto systemAppCrash = nativeCrash(START_TIME_MS + 2000,
+                CriticalEventProto.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID, 1);
+        CriticalEventProto fooAppCrash = nativeCrash(START_TIME_MS + 3000,
+                CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+        CriticalEventProto fooAppCrashUid2 = nativeCrash(START_TIME_MS + 4000,
+                CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+        CriticalEventProto fooAppCrashUid2Redacted = nativeCrash(START_TIME_MS + 4000,
+                CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+        CriticalEventProto barAppCrash = nativeCrash(START_TIME_MS + 5000,
+                CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+        CriticalEventProto barAppCrashRedacted = nativeCrash(START_TIME_MS + 5000,
+                CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+        addToLog(systemServerCrash, systemAppCrash, fooAppCrash, fooAppCrashUid2, barAppCrash);
+
+        assertProtoArrayEquals(
+                getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+                new CriticalEventProto[]{
+                        systemServerCrash,
+                        systemAppCrash,
+                        fooAppCrash,
+                        // Redacted since the trace file and crash are for different uids.
+                        fooAppCrashUid2Redacted,
+                        // Redacted since the trace file and crash are for different data apps.
+                        barAppCrashRedacted
+                });
+
+        assertProtoArrayEquals(
+                getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+                        SYSTEM_SERVER_UID).events,
+                new CriticalEventProto[]{
+                        systemServerCrash,
+                        systemAppCrash,
+                        fooAppCrash,
+                        fooAppCrashUid2,
+                        barAppCrash
+                });
+
+        assertProtoArrayEquals(
+                getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+                        SYSTEM_APP_UID).events,
+                new CriticalEventProto[]{
+                        systemServerCrash,
+                        systemAppCrash,
+                        fooAppCrash,
+                        fooAppCrashUid2,
+                        barAppCrash
+                });
+    }
+
+    @Test
+    public void privacyRedaction_doesNotMutateLogState() {
+        mCriticalEventLog.logAnr("ANR Subject", ServerProtoEnums.DATA_APP, "com.foo",
                 10_001, DATA_APP_UID);
+        mCriticalEventLog.logJavaCrash("com.foo.MyClass", ServerProtoEnums.DATA_APP, "com.foo",
+                10_001, DATA_APP_UID);
+        mCriticalEventLog.logNativeCrash(ServerProtoEnums.DATA_APP, "com.foo", 10_001,
+                DATA_APP_UID);
 
         CriticalEventLogProto unredactedLogBefore = getLogOutput(ServerProtoEnums.SYSTEM_SERVER,
                 "AID_SYSTEM", SYSTEM_SERVER_UID);
@@ -488,6 +621,12 @@
                 ServerProtoEnums.SYSTEM_SERVER);
     }
 
+    private void addToLog(CriticalEventProto... events) {
+        for (CriticalEventProto event : events) {
+            mCriticalEventLog.appendAndSave(event);
+        }
+    }
+
     private CriticalEventLogProto getLogOutput() {
         return getLogOutput(mCriticalEventLog);
     }
@@ -533,9 +672,29 @@
         }
     }
 
+    private static CriticalEventProto watchdog(long timestampMs, String subject) {
+        return watchdog(timestampMs, subject, "A UUID");
+    }
+
+    private static CriticalEventProto watchdog(long timestampMs, String subject, String uuid) {
+        CriticalEventProto event = new CriticalEventProto();
+        event.timestampMs = timestampMs;
+        event.setWatchdog(new Watchdog());
+        event.getWatchdog().subject = subject;
+        event.getWatchdog().uuid = uuid;
+        return event;
+    }
+
+    private static CriticalEventProto halfWatchdog(long timestampMs, String subject) {
+        CriticalEventProto event = new CriticalEventProto();
+        event.timestampMs = timestampMs;
+        event.setHalfWatchdog(new HalfWatchdog());
+        event.getHalfWatchdog().subject = subject;
+        return event;
+    }
+
     private static CriticalEventProto anr(long timestampMs, String subject, int processClass,
-            String processName,
-            int uid, int pid) {
+            String processName, int uid, int pid) {
         CriticalEventProto event = new CriticalEventProto();
         event.timestampMs = timestampMs;
         event.setAnr(new AppNotResponding());
@@ -547,24 +706,28 @@
         return event;
     }
 
-    private CriticalEventProto watchdog(long timestampMs, String subject) {
-        return watchdog(timestampMs, subject, "A UUID");
-    }
-
-    private CriticalEventProto watchdog(long timestampMs, String subject, String uuid) {
+    private static CriticalEventProto javaCrash(long timestampMs, String exceptionClass,
+            int processClass, String processName, int uid, int pid) {
         CriticalEventProto event = new CriticalEventProto();
         event.timestampMs = timestampMs;
-        event.setWatchdog(new Watchdog());
-        event.getWatchdog().subject = subject;
-        event.getWatchdog().uuid = uuid;
+        event.setJavaCrash(new JavaCrash());
+        event.getJavaCrash().exceptionClass = exceptionClass;
+        event.getJavaCrash().processClass = processClass;
+        event.getJavaCrash().process = processName;
+        event.getJavaCrash().uid = uid;
+        event.getJavaCrash().pid = pid;
         return event;
     }
 
-    private CriticalEventProto halfWatchdog(long timestampMs, String subject) {
+    private static CriticalEventProto nativeCrash(long timestampMs, int processClass,
+            String processName, int uid, int pid) {
         CriticalEventProto event = new CriticalEventProto();
         event.timestampMs = timestampMs;
-        event.setHalfWatchdog(new HalfWatchdog());
-        event.getHalfWatchdog().subject = subject;
+        event.setNativeCrash(new NativeCrash());
+        event.getNativeCrash().processClass = processClass;
+        event.getNativeCrash().process = processName;
+        event.getNativeCrash().uid = uid;
+        event.getNativeCrash().pid = pid;
         return event;
     }
 
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 f91661b..0f6dfda 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -36,11 +36,8 @@
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedActivityImpl;
-import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedInstrumentationImpl;
-import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedIntentInfoImpl;
-import android.content.pm.parsing.component.ParsedProvider;
 import android.content.pm.parsing.component.ParsedProviderImpl;
 import android.os.Build;
 import android.os.Process;
@@ -958,7 +955,7 @@
 
         PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
         SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
-                targetSetting.pkgFlags, targetSetting.pkgPrivateFlags);
+                targetSetting.getFlags(), targetSetting.getPrivateFlags());
         PackageSetting overlaySetting =
                 simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
         simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_APPID,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 4f77afb..f45c869 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -46,18 +46,6 @@
 import android.app.PendingIntent;
 import android.app.Person;
 import android.app.admin.DevicePolicyManager;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchManager;
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.PackageIdentifier;
-import android.app.appsearch.SearchResultPage;
-import android.app.appsearch.SetSchemaResponse;
-import android.app.appsearch.aidl.AppSearchBatchResultParcel;
-import android.app.appsearch.aidl.AppSearchResultParcel;
-import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
-import android.app.appsearch.aidl.IAppSearchManager;
-import android.app.appsearch.aidl.IAppSearchResultCallback;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
@@ -92,9 +80,7 @@
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
-import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -106,6 +92,7 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.infra.AndroidFuture;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
@@ -168,7 +155,6 @@
                 case Context.DEVICE_POLICY_SERVICE:
                     return mMockDevicePolicyManager;
                 case Context.APP_SEARCH_SERVICE:
-                    return new AppSearchManager(this, mMockAppSearchManager);
                 case Context.ROLE_SERVICE:
                     // RoleManager is final and cannot be mocked, so we only override the inject
                     // accessor methods in ShortcutService.
@@ -647,260 +633,6 @@
         }
     }
 
-    protected class MockAppSearchManager implements IAppSearchManager {
-
-        protected Map<String, List<PackageIdentifier>> mSchemasVisibleToPackages =
-                new ArrayMap<>(1);
-        private Map<String, Map<String, GenericDocument>> mDocumentMap = new ArrayMap<>(1);
-
-        private String getKey(UserHandle userHandle, String databaseName) {
-            return userHandle.getIdentifier() + "@" + databaseName;
-        }
-
-        @Override
-        public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles,
-                List<String> schemasNotDisplayedBySystem,
-                Map<String, List<Bundle>> schemasVisibleToPackagesBundles, boolean forceOverride,
-                int version, UserHandle userHandle, long binderCallStartTimeMillis,
-                IAppSearchResultCallback callback) throws RemoteException {
-            for (Map.Entry<String, List<Bundle>> entry :
-                    schemasVisibleToPackagesBundles.entrySet()) {
-                final String key = entry.getKey();
-                final List<PackageIdentifier> packageIdentifiers;
-                if (!mSchemasVisibleToPackages.containsKey(key)) {
-                    packageIdentifiers = new ArrayList<>(entry.getValue().size());
-                    mSchemasVisibleToPackages.put(key, packageIdentifiers);
-                } else {
-                    packageIdentifiers = mSchemasVisibleToPackages.get(key);
-                }
-                for (int i = 0; i < entry.getValue().size(); i++) {
-                    packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
-                }
-            }
-            final SetSchemaResponse response = new SetSchemaResponse.Builder().build();
-            callback.onResult(
-                    new AppSearchResultParcel(
-                            AppSearchResult.newSuccessfulResult(response.getBundle())));
-        }
-
-        @Override
-        public void getSchema(String packageName, String databaseName, UserHandle userHandle,
-                IAppSearchResultCallback callback) throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public void getNamespaces(String packageName, String databaseName, UserHandle userHandle,
-                IAppSearchResultCallback callback) throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public void putDocuments(String packageName, String databaseName,
-                List<Bundle> documentBundles, UserHandle userHandle, long binderCallStartTimeMillis,
-                IAppSearchBatchResultCallback callback)
-                throws RemoteException {
-            final List<GenericDocument> docs = new ArrayList<>(documentBundles.size());
-            for (Bundle bundle : documentBundles) {
-                docs.add(new GenericDocument(bundle));
-            }
-            final AppSearchBatchResult.Builder<String, Void> builder =
-                    new AppSearchBatchResult.Builder<>();
-            final String key = getKey(userHandle, databaseName);
-            Map<String, GenericDocument> docMap = mDocumentMap.get(key);
-            for (GenericDocument doc : docs) {
-                builder.setSuccess(doc.getId(), null);
-                if (docMap == null) {
-                    docMap = new ArrayMap<>(1);
-                    mDocumentMap.put(key, docMap);
-                }
-                docMap.put(doc.getId(), doc);
-            }
-            callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
-        }
-
-        @Override
-        public void getDocuments(String packageName, String databaseName, String namespace,
-                List<String> ids, Map<String, List<String>> typePropertyPaths,
-                UserHandle userHandle,  long binderCallStartTimeMillis,
-                IAppSearchBatchResultCallback callback) throws RemoteException {
-            final AppSearchBatchResult.Builder<String, Bundle> builder =
-                    new AppSearchBatchResult.Builder<>();
-            final String key = getKey(userHandle, databaseName);
-            if (!mDocumentMap.containsKey(key)) {
-                for (String id : ids) {
-                    builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
-                            key + " not found when getting: " + id);
-                }
-            } else {
-                final Map<String, GenericDocument> docs = mDocumentMap.get(key);
-                for (String id : ids) {
-                    if (docs.containsKey(id)) {
-                        builder.setSuccess(id, docs.get(id).getBundle());
-                    } else {
-                        builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
-                                "shortcut not found: " + id);
-                    }
-                }
-            }
-            callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
-        }
-
-        @Override
-        public void query(String packageName, String databaseName, String queryExpression,
-                Bundle searchSpecBundle, UserHandle userHandle, long binderCallStartTimeMillis,
-                IAppSearchResultCallback callback)
-                throws RemoteException {
-            final String key = getKey(userHandle, databaseName);
-            if (!mDocumentMap.containsKey(key)) {
-                final Bundle page = new Bundle();
-                page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
-                page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
-                callback.onResult(
-                        new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
-                return;
-            }
-            final List<GenericDocument> documents = new ArrayList<>(mDocumentMap.get(key).values());
-            final Bundle page = new Bundle();
-            page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 0);
-            final ArrayList<Bundle> resultBundles = new ArrayList<>();
-            for (GenericDocument document : documents) {
-                final Bundle resultBundle = new Bundle();
-                resultBundle.putBundle("document", document.getBundle());
-                resultBundle.putString("packageName", packageName);
-                resultBundle.putString("databaseName", databaseName);
-                resultBundle.putParcelableArrayList("matches", new ArrayList<>());
-                resultBundles.add(resultBundle);
-            }
-            page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
-            callback.onResult(
-                    new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
-        }
-
-        @Override
-        public void globalQuery(String packageName, String queryExpression, Bundle searchSpecBundle,
-                UserHandle userHandle, long binderCallStartTimeMillis,
-                IAppSearchResultCallback callback) throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public void getNextPage(String packageName, long nextPageToken, UserHandle userHandle,
-                IAppSearchResultCallback callback) throws RemoteException {
-            final Bundle page = new Bundle();
-            page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
-            page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
-            callback.onResult(
-                    new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
-        }
-
-        @Override
-        public void invalidateNextPageToken(String packageName, long nextPageToken,
-                UserHandle userHandle) throws RemoteException {
-        }
-
-        @Override
-        public void writeQueryResultsToFile(String packageName, String databaseName,
-                ParcelFileDescriptor fileDescriptor, String queryExpression,
-                Bundle searchSpecBundle, UserHandle userHandle, IAppSearchResultCallback callback)
-                throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public void putDocumentsFromFile(String packageName, String databaseName,
-                ParcelFileDescriptor fileDescriptor, UserHandle userHandle,
-                IAppSearchResultCallback callback)
-                throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public void reportUsage(String packageName, String databaseName, String namespace,
-                String documentId, long usageTimestampMillis, boolean systemUsage,
-                UserHandle userHandle,
-                IAppSearchResultCallback callback)
-                throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public void removeByDocumentId(String packageName, String databaseName, String namespace,
-                List<String> ids, UserHandle userHandle, long binderCallStartTimeMillis,
-                IAppSearchBatchResultCallback callback)
-                throws RemoteException {
-            final AppSearchBatchResult.Builder<String, Void> builder =
-                    new AppSearchBatchResult.Builder<>();
-            final String key = getKey(userHandle, databaseName);
-            if (!mDocumentMap.containsKey(key)) {
-                for (String id : ids) {
-                    builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
-                            "package " + key + " not found when removing " + id);
-                }
-            } else {
-                final Map<String, GenericDocument> docs = mDocumentMap.get(key);
-                for (String id : ids) {
-                    if (docs.containsKey(id)) {
-                        docs.remove(id);
-                        builder.setSuccess(id, null);
-                    } else {
-                        builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
-                                "shortcut not found when removing " + id);
-                    }
-                }
-            }
-            callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
-        }
-
-        @Override
-        public void removeByQuery(String packageName, String databaseName, String queryExpression,
-                Bundle searchSpecBundle, UserHandle userHandle, long binderCallStartTimeMillis,
-                IAppSearchResultCallback callback)
-                throws RemoteException {
-            final String key = getKey(userHandle, databaseName);
-            if (!mDocumentMap.containsKey(key)) {
-                callback.onResult(
-                        new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
-                return;
-            }
-            mDocumentMap.get(key).clear();
-            callback.onResult(
-                    new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
-        }
-
-        @Override
-        public void getStorageInfo(String packageName, String databaseName, UserHandle userHandle,
-                IAppSearchResultCallback callback) throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public void persistToDisk(String packageName, UserHandle userHandle,
-                long binderCallStartTimeMillis) throws RemoteException {
-        }
-
-        @Override
-        public void initialize(String packageName, UserHandle userHandle,
-                long binderCallStartTimeMillis, IAppSearchResultCallback callback)
-                throws RemoteException {
-            ignore(callback);
-        }
-
-        @Override
-        public IBinder asBinder() {
-            return null;
-        }
-
-        private void removeShortcuts() {
-            mDocumentMap.clear();
-        }
-
-        private void ignore(IAppSearchResultCallback callback) throws RemoteException {
-            callback.onResult(
-                    new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
-        }
-    }
-
     public static class ShortcutActivity extends Activity {
     }
 
@@ -952,7 +684,6 @@
     protected PackageManagerInternal mMockPackageManagerInternal;
     protected UserManager mMockUserManager;
     protected DevicePolicyManager mMockDevicePolicyManager;
-    protected MockAppSearchManager mMockAppSearchManager;
     protected UserManagerInternal mMockUserManagerInternal;
     protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
     protected ActivityManagerInternal mMockActivityManagerInternal;
@@ -1102,7 +833,6 @@
         mMockPackageManagerInternal = mock(PackageManagerInternal.class);
         mMockUserManager = mock(UserManager.class);
         mMockDevicePolicyManager = mock(DevicePolicyManager.class);
-        mMockAppSearchManager = new MockAppSearchManager();
         mMockUserManagerInternal = mock(UserManagerInternal.class);
         mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
         mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
@@ -1314,9 +1044,6 @@
 
         shutdownServices();
 
-        mMockAppSearchManager.removeShortcuts();
-        mMockAppSearchManager = null;
-
         super.tearDown();
     }
 
@@ -2235,6 +1962,18 @@
         return p == null ? null : p.getAllShareTargetsForTest();
     }
 
+    protected void resetPersistedShortcuts() {
+        final ShortcutPackage p = mService.getPackageShortcutForTest(
+                getCallingPackage(), getCallingUserId());
+        p.removeAllShortcutsAsync();
+    }
+
+    protected void getPersistedShortcut(AndroidFuture<List<ShortcutInfo>> cb) {
+        final ShortcutPackage p = mService.getPackageShortcutForTest(
+                getCallingPackage(), getCallingUserId());
+        p.getTopShortcutsFromPersistence(cb);
+    }
+
     /**
      * @return the number of shortcuts stored internally for the caller that can be used as a share
      * target in the ShareSheet. Such shortcuts have a matching category with at least one of the
@@ -2425,8 +2164,6 @@
 
         deleteAllSavedFiles();
 
-        mMockAppSearchManager.removeShortcuts();
-
         initService();
         mService.applyRestore(payload, USER_0);
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index b0f8d40..c1cf007 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -82,7 +82,10 @@
 import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 
 @RunWith(AndroidJUnit4.class)
@@ -439,14 +442,14 @@
                 .setUid(ps2.getAppId())
                 .hideAsFinal());
 
-        ps1.usesStaticLibraries = new String[] { "com.example.shared.one" };
-        ps1.usesStaticLibrariesVersions = new long[] { 12 };
-        ps1.setFlags(ps1.pkgFlags | ApplicationInfo.FLAG_SYSTEM);
+        ps1.setUsesStaticLibraries(new String[] { "com.example.shared.one" });
+        ps1.setUsesStaticLibrariesVersions(new long[] { 12 });
+        ps1.setFlags(ps1.getFlags() | ApplicationInfo.FLAG_SYSTEM);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
         assertThat(settingsUnderTest.disableSystemPackageLPw(PACKAGE_NAME_1, false), is(true));
 
-        ps2.usesStaticLibraries = new String[] { "com.example.shared.two" };
-        ps2.usesStaticLibrariesVersions = new long[] { 34 };
+        ps2.setUsesStaticLibraries(new String[] { "com.example.shared.two" });
+        ps2.setUsesStaticLibrariesVersions(new long[] { 34 });
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
 
         settingsUnderTest.writeLPr();
@@ -460,32 +463,32 @@
         PackageSetting readPs2 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_2);
 
         Truth.assertThat(readPs1).isNotNull();
-        Truth.assertThat(readPs1.usesStaticLibraries).isNotNull();
-        Truth.assertThat(readPs1.usesStaticLibrariesVersions).isNotNull();
+        Truth.assertThat(readPs1.getUsesStaticLibraries()).isNotNull();
+        Truth.assertThat(readPs1.getUsesStaticLibrariesVersions()).isNotNull();
         Truth.assertThat(readPs2).isNotNull();
-        Truth.assertThat(readPs2.usesStaticLibraries).isNotNull();
-        Truth.assertThat(readPs2.usesStaticLibrariesVersions).isNotNull();
+        Truth.assertThat(readPs2.getUsesStaticLibraries()).isNotNull();
+        Truth.assertThat(readPs2.getUsesStaticLibrariesVersions()).isNotNull();
 
         List<Long> ps1VersionsAsList = new ArrayList<>();
-        for (long version : ps1.usesStaticLibrariesVersions) {
+        for (long version : ps1.getUsesStaticLibrariesVersions()) {
             ps1VersionsAsList.add(version);
         }
 
         List<Long> ps2VersionsAsList = new ArrayList<>();
-        for (long version : ps2.usesStaticLibrariesVersions) {
+        for (long version : ps2.getUsesStaticLibrariesVersions()) {
             ps2VersionsAsList.add(version);
         }
 
-        Truth.assertThat(readPs1.usesStaticLibraries).asList()
-                .containsExactlyElementsIn(ps1.usesStaticLibraries).inOrder();
+        Truth.assertThat(readPs1.getUsesStaticLibraries()).asList()
+                .containsExactlyElementsIn(ps1.getUsesStaticLibraries()).inOrder();
 
-        Truth.assertThat(readPs1.usesStaticLibrariesVersions).asList()
+        Truth.assertThat(readPs1.getUsesStaticLibrariesVersions()).asList()
                 .containsExactlyElementsIn(ps1VersionsAsList).inOrder();
 
-        Truth.assertThat(readPs2.usesStaticLibraries).asList()
-                .containsExactlyElementsIn(ps2.usesStaticLibraries).inOrder();
+        Truth.assertThat(readPs2.getUsesStaticLibraries()).asList()
+                .containsExactlyElementsIn(ps2.getUsesStaticLibraries()).inOrder();
 
-        Truth.assertThat(readPs2.usesStaticLibrariesVersions).asList()
+        Truth.assertThat(readPs2.getUsesStaticLibrariesVersions()).asList()
                 .containsExactlyElementsIn(ps2VersionsAsList).inOrder();
     }
 
@@ -615,8 +618,8 @@
         final PackageSetting testPkgSetting01 =
                 createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
         testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        assertThat(testPkgSetting01.getFlags(), is(0));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(0));
         final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
         Settings.updatePackageSetting(
                 testPkgSetting01,
@@ -635,8 +638,8 @@
                 UUID.randomUUID());
         assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
         assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        assertThat(testPkgSetting01.getFlags(), is(0));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(0));
         final PackageUserState userState = testPkgSetting01.readUserState(0);
         final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
         verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
@@ -649,8 +652,8 @@
         final PackageSetting testPkgSetting01 =
                 createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
         testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        assertThat(testPkgSetting01.getFlags(), is(0));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(0));
         final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
         Settings.updatePackageSetting(
                 testPkgSetting01,
@@ -669,8 +672,8 @@
                 UUID.randomUUID());
         assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
         assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
-        assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
+        assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
         final PackageUserState userState = testPkgSetting01.readUserState(0);
         final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
         // WARNING: When creating a shallow copy of the PackageSetting we do NOT create
@@ -739,8 +742,8 @@
                 UUID.randomUUID());
         assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
-        assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
+        assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
         assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
         assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
         // signatures object must be different
@@ -779,8 +782,8 @@
         assertThat(testPkgSetting01.getAppId(), is(0));
         assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        assertThat(testPkgSetting01.getFlags(), is(0));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(0));
         assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
         assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
         assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
@@ -821,8 +824,8 @@
         assertThat(testPkgSetting01.getAppId(), is(10064));
         assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        assertThat(testPkgSetting01.getFlags(), is(0));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(0));
         assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
         assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
         assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
@@ -863,8 +866,8 @@
         assertThat(testPkgSetting01.getAppId(), is(10064));
         assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
-        assertThat(testPkgSetting01.pkgFlags, is(0));
-        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+        assertThat(testPkgSetting01.getFlags(), is(0));
+        assertThat(testPkgSetting01.getPrivateFlags(), is(0));
         assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
         assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
         assertNotSame(testPkgSetting01.getSignatures(), disabledSignatures);
@@ -930,10 +933,11 @@
                 testPkgSetting.getLegacyNativeLibraryPath());
         assertThat(origPkgSetting.getLegacyNativeLibraryPath(),
                 is(testPkgSetting.getLegacyNativeLibraryPath()));
-        if (origPkgSetting.mimeGroups != null) {
-            assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+        if (origPkgSetting.getMimeGroups() != null
+                && origPkgSetting.getMimeGroups() != Collections.<String, Set<String>>emptyMap()) {
+            assertNotSame(origPkgSetting.getMimeGroups(), testPkgSetting.getMimeGroups());
         }
-        assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups));
+        assertThat(origPkgSetting.getMimeGroups(), is(testPkgSetting.getMimeGroups()));
         assertNotSame(origPkgSetting.mLegacyPermissionsState,
                 testPkgSetting.mLegacyPermissionsState);
         assertThat(origPkgSetting.mLegacyPermissionsState,
@@ -945,8 +949,8 @@
         assertSame(origPkgSetting.getPkg(), testPkgSetting.getPkg());
         // No equals() method for this object
         // assertThat(origPkgSetting.pkg, is(testPkgSetting.pkg));
-        assertThat(origPkgSetting.pkgFlags, is(testPkgSetting.pkgFlags));
-        assertThat(origPkgSetting.pkgPrivateFlags, is(testPkgSetting.pkgPrivateFlags));
+        assertThat(origPkgSetting.getFlags(), is(testPkgSetting.getFlags()));
+        assertThat(origPkgSetting.getPrivateFlags(), is(testPkgSetting.getPrivateFlags()));
         assertSame(origPkgSetting.getPrimaryCpuAbi(), testPkgSetting.getPrimaryCpuAbi());
         assertThat(origPkgSetting.getPrimaryCpuAbi(), is(testPkgSetting.getPrimaryCpuAbi()));
         assertThat(origPkgSetting.getRealName(), is(testPkgSetting.getRealName()));
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 5593763..2146070 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -17,7 +17,6 @@
 package com.android.server.pm;
 
 import android.content.pm.SigningDetails;
-import android.util.ArraySet;
 import android.util.SparseArray;
 
 import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -25,6 +24,7 @@
 
 import java.io.File;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 
 public class PackageSettingBuilder {
@@ -46,7 +46,7 @@
     private InstallSource mInstallSource;
     private String[] mUsesStaticLibraries;
     private long[] mUsesStaticLibrariesVersions;
-    private Map<String, ArraySet<String>> mMimeGroups;
+    private Map<String, Set<String>> mMimeGroups;
     private SigningDetails mSigningDetails;
     private UUID mDomainSetId = UUID.randomUUID();
 
@@ -127,7 +127,7 @@
         return this;
     }
 
-    public PackageSettingBuilder setMimeGroups(Map<String, ArraySet<String>> mimeGroups) {
+    public PackageSettingBuilder setMimeGroups(Map<String, Set<String>> mimeGroups) {
         this.mMimeGroups = mimeGroups;
         return this;
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 82ee330..cfdbb5b7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -554,9 +554,9 @@
             String packageName, boolean isInstant, PackageSetting pkgSetting) {
         assertThat(pkgSetting.getPkg().getPackageName(), is(packageName));
         assertThat(pkgSetting.getInstantApp(0), is(isInstant));
-        assertThat(pkgSetting.usesStaticLibraries,
+        assertThat(pkgSetting.getUsesStaticLibraries(),
                 arrayContaining("some.static.library", "some.other.static.library"));
-        assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
+        assertThat(pkgSetting.getUsesStaticLibrariesVersions(), is(new long[]{234L, 456L}));
         assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage));
         assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
         assertThat(pkgSetting.getVersionCode(),
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 9598a00..bc2d256 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -18,9 +18,20 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
 
 import android.app.PendingIntent;
+import android.content.pm.ShortcutInfo;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 /**
  * Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
  *
@@ -28,6 +39,21 @@
  */
 public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mService.updateConfigurationLocked(
+                ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
+                        + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_0).removeAllShortcutsAsync();
+        super.tearDown();
+    }
+
     public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
         setDefaultLauncher(USER_0, LAUNCHER_1);
 
@@ -41,4 +67,201 @@
             assertNotNull(intent);
         });
     }
+
+    public void testSetDynamicShortcuts_PersistsShortcutsToDisk() throws RemoteException {
+        if (!mService.isAppSearchEnabled()) {
+            return;
+        }
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        // Verifies setDynamicShortcuts persists shortcuts into AppSearch
+        mManager.setDynamicShortcuts(list(
+                makeShortcut("s1"),
+                makeShortcut("s2"),
+                makeShortcut("s3")
+        ));
+        List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(3, shortcuts.size());
+        Set<String> shortcutIds =
+                shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+        assertTrue(shortcutIds.contains("s1"));
+        assertTrue(shortcutIds.contains("s2"));
+        assertTrue(shortcutIds.contains("s3"));
+
+        // Verifies removeAllDynamicShortcuts removes shortcuts from persistence layer
+        mManager.removeAllDynamicShortcuts();
+        shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertTrue(shortcuts.isEmpty());
+    }
+
+    public void testAddDynamicShortcuts_PersistsShortcutsToDisk() {
+        if (!mService.isAppSearchEnabled()) {
+            return;
+        }
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mManager.setDynamicShortcuts(list(
+                makeShortcut("s1"),
+                makeShortcut("s2"),
+                makeShortcut("s3")
+        ));
+        // Verifies addDynamicShortcuts persists shortcuts into AppSearch
+        mManager.addDynamicShortcuts(list(makeShortcut("s4"), makeShortcut("s5")));
+        final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(5, shortcuts.size());
+        final Set<String> shortcutIds =
+                shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+        assertTrue(shortcutIds.contains("s1"));
+        assertTrue(shortcutIds.contains("s2"));
+        assertTrue(shortcutIds.contains("s3"));
+        assertTrue(shortcutIds.contains("s4"));
+        assertTrue(shortcutIds.contains("s5"));
+    }
+
+    public void testPushDynamicShortcuts_PersistsShortcutsToDisk() {
+        if (!mService.isAppSearchEnabled()) {
+            return;
+        }
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mManager.setDynamicShortcuts(list(
+                makeShortcut("s1"),
+                makeShortcut("s2"),
+                makeShortcut("s3"),
+                makeShortcut("s4"),
+                makeShortcut("s5")
+        ));
+        List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(5, shortcuts.size());
+        Set<String> shortcutIds =
+                shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+        assertTrue(shortcutIds.contains("s1"));
+        assertTrue(shortcutIds.contains("s2"));
+        assertTrue(shortcutIds.contains("s3"));
+        assertTrue(shortcutIds.contains("s4"));
+        assertTrue(shortcutIds.contains("s5"));
+        // Verifies pushDynamicShortcuts further persists shortcuts into AppSearch without
+        // removing previous shortcuts when max number of shortcuts is reached.
+        mManager.pushDynamicShortcut(makeShortcut("s6"));
+        shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(6, shortcuts.size());
+        shortcutIds = shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+        assertTrue(shortcutIds.contains("s1"));
+        assertTrue(shortcutIds.contains("s2"));
+        assertTrue(shortcutIds.contains("s3"));
+        assertTrue(shortcutIds.contains("s4"));
+        assertTrue(shortcutIds.contains("s5"));
+        assertTrue(shortcutIds.contains("s6"));
+    }
+
+    public void testRemoveDynamicShortcuts_RemovesShortcutsFromDisk() {
+        if (!mService.isAppSearchEnabled()) {
+            return;
+        }
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mManager.setDynamicShortcuts(list(
+                makeShortcut("s1"),
+                makeShortcut("s2"),
+                makeShortcut("s3"),
+                makeShortcut("s4"),
+                makeShortcut("s5")
+        ));
+
+        // Verifies removeDynamicShortcuts removes shortcuts from persistence layer
+        mManager.removeDynamicShortcuts(list("s1"));
+        final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(4, shortcuts.size());
+        final Set<String> shortcutIds =
+                shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+        assertTrue(shortcutIds.contains("s2"));
+        assertTrue(shortcutIds.contains("s3"));
+        assertTrue(shortcutIds.contains("s4"));
+        assertTrue(shortcutIds.contains("s5"));
+    }
+
+    public void testRemoveLongLivedShortcuts_RemovesShortcutsFromDisk() {
+        if (!mService.isAppSearchEnabled()) {
+            return;
+        }
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mManager.setDynamicShortcuts(list(
+                makeShortcut("s1"),
+                makeShortcut("s2"),
+                makeShortcut("s3"),
+                makeShortcut("s4"),
+                makeShortcut("s5")
+        ));
+        mManager.removeDynamicShortcuts(list("s2"));
+        final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(4, shortcuts.size());
+        final Set<String> shortcutIds =
+                shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+        assertTrue(shortcutIds.contains("s1"));
+        assertTrue(shortcutIds.contains("s3"));
+        assertTrue(shortcutIds.contains("s4"));
+        assertTrue(shortcutIds.contains("s5"));
+    }
+
+    public void testDisableShortcuts_RemovesShortcutsFromDisk() {
+        if (!mService.isAppSearchEnabled()) {
+            return;
+        }
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mManager.setDynamicShortcuts(list(
+                makeShortcut("s1"),
+                makeShortcut("s2"),
+                makeShortcut("s3"),
+                makeShortcut("s4"),
+                makeShortcut("s5")
+        ));
+        // Verifies disableShortcuts removes shortcuts from persistence layer
+        mManager.disableShortcuts(list("s3"));
+        final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(4, shortcuts.size());
+        final Set<String> shortcutIds =
+                shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+        assertTrue(shortcutIds.contains("s1"));
+        assertTrue(shortcutIds.contains("s2"));
+        assertTrue(shortcutIds.contains("s4"));
+        assertTrue(shortcutIds.contains("s5"));
+    }
+
+    public void testUpdateShortcuts_UpdateShortcutsOnDisk() {
+        if (!mService.isAppSearchEnabled()) {
+            return;
+        }
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mManager.setDynamicShortcuts(list(
+                makeShortcut("s1"),
+                makeShortcut("s2"),
+                makeShortcut("s3"),
+                makeShortcut("s4"),
+                makeShortcut("s5")
+        ));
+        // Verifies disableShortcuts removes shortcuts from persistence layer
+        mManager.updateShortcuts(list(makeShortcutWithShortLabel("s3", "custom")));
+        final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+        assertNotNull(shortcuts);
+        assertEquals(5, shortcuts.size());
+        final Map<String, ShortcutInfo> map = shortcuts.stream()
+                .collect(Collectors.toMap(ShortcutInfo::getId, Function.identity()));
+        assertTrue(map.containsKey("s3"));
+        assertEquals("custom", map.get("s3").getShortLabel());
+    }
+
+    private List<ShortcutInfo> getAllPersistedShortcuts() {
+        try {
+            SystemClock.sleep(500);
+            final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
+            getPersistedShortcut(future);
+            return future.get(10, TimeUnit.SECONDS);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ea46eab..d593e80 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -32,7 +32,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
@@ -40,6 +39,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -48,6 +48,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
@@ -73,7 +74,6 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
@@ -102,6 +102,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
 public class BuzzBeepBlinkTest extends UiServiceTestCase {
 
     @Mock AudioManager mAudioManager;
@@ -156,6 +157,7 @@
         when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+        when(mAudioManager.getFocusRampTimeMs(anyInt(), any(AudioAttributes.class))).thenReturn(50);
         when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
         when(mVibrator.hasFrequencyControl()).thenReturn(false);
         when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
@@ -444,6 +446,11 @@
                 timeout(MAX_VIBRATION_DELAY).times(1));
     }
 
+    private void verifyDelayedNeverVibrate() {
+        verify(mVibrator, after(MAX_VIBRATION_DELAY).never()).vibrate(anyInt(), anyString(), any(),
+                anyString(), any(AudioAttributes.class));
+    }
+
     private void verifyVibrate(ArgumentMatcher<VibrationEffect> effectMatcher,
             VerificationMode verification) {
         ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
@@ -1588,8 +1595,51 @@
         // beep wasn't reset
         verifyNeverBeep();
         verifyNeverVibrate();
-        verify(mRingtonePlayer, never()).stopAsync();
-        verify(mVibrator, never()).cancel();
+        verifyNeverStopAudio();
+        verifyNeverStopVibrate();
+    }
+
+    @Test
+    public void testRingtoneInsistentBeep_clearEffectsStopsSoundAndVibration() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+        mService.addNotification(ringtoneNotification);
+        assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+        verifyDelayedVibrateLooped();
+
+        mService.clearSoundLocked();
+        mService.clearVibrateLocked();
+
+        verifyStopAudio();
+        verifyStopVibrate();
+    }
+
+    @Test
+    public void testRingtoneInsistentBeep_neverVibratesWhenEffectsClearedBeforeDelay()
+            throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+        mService.addNotification(ringtoneNotification);
+        assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+        verifyNeverVibrate();
+
+        mService.clearSoundLocked();
+        mService.clearVibrateLocked();
+
+        verifyStopAudio();
+        verifyDelayedNeverVibrate();
     }
 
     @Test
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 7237b24..31e7ad0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -142,6 +142,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.Icon;
 import android.media.AudioManager;
+import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -8465,4 +8466,51 @@
             fail("call to matchesCallFilter with listener permissions should work");
         }
     }
+
+    @Test
+    public void testMediaNotificationsBypassBlock() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.setNotificationsEnabledForPackage(
+                r.getSbn().getPackageName(), r.getUid(), false);
+
+        // normal blocked notifications - blocked
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+        // just using the style - blocked
+        nb.setStyle(new Notification.MediaStyle());
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+        // style + media session - bypasses block
+        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+    }
+
+    @Test
+    public void testGetAllUsersNotificationPermissions_migrationNotEnabled() {
+        // make sure we don't bother if the migration is not enabled
+        assertThat(mService.getAllUsersNotificationPermissions()).isNull();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 423ba94..ea01963 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -20,9 +20,9 @@
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.UserHandle.USER_SYSTEM;
@@ -36,6 +36,7 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -82,8 +83,10 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.media.AudioManager;
+import android.media.session.MediaSession;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
@@ -102,8 +105,10 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestablePermissions;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.Pair;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -125,7 +130,6 @@
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -711,4 +715,132 @@
 
         assertThat(r.isImportanceFixed()).isTrue();
     }
+
+    @Test
+    public void testMediaNotificationsBypassBlock() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+        // normal blocked notifications - blocked
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+        // just using the style - blocked
+        nb.setStyle(new Notification.MediaStyle());
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+        // style + media session - bypasses block
+        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+    }
+
+    @Test
+    public void testMediaNotificationsBypassBlock_atPost() throws Exception {
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
+
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(r.getKey());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+
+        // just using the style - blocked
+        mService.clearNotifications();
+        reset(mUsageStats);
+        nb.setStyle(new Notification.MediaStyle());
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addEnqueuedNotification(r);
+        runnable = mService.new PostNotificationRunnable(r.getKey());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+
+        // style + media session - bypasses block
+        mService.clearNotifications();
+        reset(mUsageStats);
+        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addEnqueuedNotification(r);
+        runnable = mService.new PostNotificationRunnable(r.getKey());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats, never()).registerBlocked(any());
+        verify(mUsageStats).registerPostedByApp(any());
+    }
+
+    @Test
+    public void testGetAllUsersNotificationPermissions() {
+        // In this case, there are multiple users each with notification permissions (and also,
+        // for good measure, some without).
+        // make sure the collection returned contains info for all of them
+        final List<UserInfo> userInfos = new ArrayList<>();
+        userInfos.add(new UserInfo(0, "user0", 0));
+        userInfos.add(new UserInfo(1, "user1", 0));
+        userInfos.add(new UserInfo(2, "user2", 0));
+        when(mUm.getUsers()).thenReturn(userInfos);
+
+        // construct the permissions for each of them
+        ArrayMap<Pair<Integer, String>, Boolean> permissions0 = new ArrayMap<>(),
+                permissions1 = new ArrayMap<>();
+        permissions0.put(new Pair<>(10, "package1"), true);
+        permissions0.put(new Pair<>(20, "package2"), false);
+        permissions1.put(new Pair<>(11, "package1"), false);
+        permissions1.put(new Pair<>(21, "package2"), true);
+        when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0);
+        when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1);
+        when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>());
+
+        ArrayMap<Pair<Integer, String>, Boolean> combinedPermissions =
+                mService.getAllUsersNotificationPermissions();
+        assertTrue(combinedPermissions.get(new Pair<>(10, "package1")));
+        assertFalse(combinedPermissions.get(new Pair<>(20, "package2")));
+        assertFalse(combinedPermissions.get(new Pair<>(11, "package1")));
+        assertTrue(combinedPermissions.get(new Pair<>(21, "package2")));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index b89a94a..dd6d469 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -34,6 +34,7 @@
 import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IMPORTANCE_FIELD_NUMBER;
@@ -97,6 +98,7 @@
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.service.notification.ConversationChannelWrapper;
+import android.service.notification.nano.RankingHelperProto;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableContentResolver;
 import android.text.format.DateUtils;
@@ -104,20 +106,19 @@
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Pair;
-import android.util.Slog;
 import android.util.StatsEvent;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.os.AtomsProto.PackageNotificationPreferences;
 import com.android.server.UiServiceTestCase;
 import com.android.server.notification.PermissionHelper.PackagePermission;
 
-import com.google.common.collect.ImmutableMap;
-
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.junit.Before;
@@ -130,6 +131,8 @@
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -2545,6 +2548,416 @@
     }
 
     @Test
+    public void testDumpJson_prePermissionMigration() throws Exception {
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+        // before the migration is active, we want to verify that:
+        //   - all notification importance info should come from package preferences
+        //   - if there are permissions granted or denied from packages PreferencesHelper doesn't
+        //     know about, those are ignored if migration is not enabled
+
+        // package permissions map to be passed in
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_P, PKG_P), true);  // in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel3 = new NotificationChannel("id3", "name3", IMPORTANCE_HIGH);
+
+        mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE);
+        mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+
+        // in the json array, all of the individual package preferences are simply elements in the
+        // values array. this set is to collect expected outputs for each of our packages.
+        // the key/value pairs are: (userId, package name) -> expected importance
+        ArrayMap<Pair<Integer, String>, String> expected = new ArrayMap<>();
+        expected.put(new Pair(UserHandle.getUserId(UID_P), PKG_P), "LOW");
+        expected.put(new Pair(UserHandle.getUserId(UID_O), PKG_O), "HIGH");
+        expected.put(new Pair(UserHandle.getUserId(UID_N_MR1), PKG_N_MR1), "NONE");
+
+        JSONArray actual = (JSONArray) mHelper.dumpJson(
+                new NotificationManagerService.DumpFilter(), appPermissions)
+                .get("PackagePreferencess");
+        assertThat(actual.length()).isEqualTo(expected.size());
+        for (int i = 0; i < actual.length(); i++) {
+            JSONObject pkgInfo = actual.getJSONObject(i);
+            Pair<Integer, String> pkgKey =
+                    new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
+            assertTrue(expected.containsKey(pkgKey));
+            assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
+        }
+
+        // also make sure that (more likely to actually happen) if we don't provide an array of
+        // app preferences (and do null instead), the same thing happens, so do the same checks
+        JSONArray actualWithNullInput = (JSONArray) mHelper.dumpJson(
+                new NotificationManagerService.DumpFilter(), null)
+                .get("PackagePreferencess");
+        assertThat(actualWithNullInput.length()).isEqualTo(expected.size());
+        for (int i = 0; i < actualWithNullInput.length(); i++) {
+            JSONObject pkgInfo = actualWithNullInput.getJSONObject(i);
+            Pair<Integer, String> pkgKey =
+                    new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
+            assertTrue(expected.containsKey(pkgKey));
+            assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
+        }
+    }
+
+    @Test
+    public void testDumpJson_postPermissionMigration() throws Exception {
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+        // when getting a json dump, we want to verify that:
+        //   - all notification importance info should come from the permission, even if the data
+        //     isn't there yet but is present in package preferences
+        //   - if there are permissions granted or denied from packages PreferencesHelper doesn't
+        //     know about, those should still be included
+
+        // package permissions map to be passed in
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_P, PKG_P), true);  // in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        NotificationChannel channel3 = new NotificationChannel("id3", "name3", IMPORTANCE_HIGH);
+
+        mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false);
+        mHelper.createNotificationChannel(PKG_P, UID_P, channel2, false, false);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+        mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+
+        // in the json array, all of the individual package preferences are simply elements in the
+        // values array. this set is to collect expected outputs for each of our packages.
+        // the key/value pairs are: (userId, package name) -> expected importance
+        ArrayMap<Pair<Integer, String>, String> expected = new ArrayMap<>();
+
+        // packages that only exist via the app permissions; should be present
+        expected.put(new Pair(UserHandle.getUserId(1), "first"), "DEFAULT");
+        expected.put(new Pair(UserHandle.getUserId(3), "third"), "NONE");
+
+        // packages that exist in both app permissions & local preferences
+        expected.put(new Pair(UserHandle.getUserId(UID_P), PKG_P), "DEFAULT");
+        expected.put(new Pair(UserHandle.getUserId(UID_O), PKG_O), "NONE");
+
+        // package that only exists in local preferences; expect no importance output
+        expected.put(new Pair(UserHandle.getUserId(UID_N_MR1), PKG_N_MR1), null);
+
+        JSONArray actual = (JSONArray) mHelper.dumpJson(
+                new NotificationManagerService.DumpFilter(), appPermissions)
+                .get("PackagePreferencess");
+        assertThat(actual.length()).isEqualTo(expected.size());
+        for (int i = 0; i < actual.length(); i++) {
+            JSONObject pkgInfo = actual.getJSONObject(i);
+            Pair<Integer, String> pkgKey =
+                    new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
+            assertTrue(expected.containsKey(pkgKey));
+            if (pkgInfo.has("importance")) {
+                assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
+            } else {
+                assertThat(expected.get(pkgKey)).isNull();
+            }
+        }
+    }
+
+    @Test
+    public void testDumpJson_givenNullInput_postMigration() throws Exception {
+        // simple test just to make sure nothing dies if we pass in null input even post migration
+        // for some reason, even though in practice this should not be how one calls this method
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+        // some packages exist, with some importance info that won't be looked at
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        JSONArray actual = (JSONArray) mHelper.dumpJson(
+                new NotificationManagerService.DumpFilter(), null)
+                .get("PackagePreferencess");
+
+        // there should still be info for the packages
+        assertThat(actual.length()).isEqualTo(2);
+
+        // but they should not have importance info because the migration is enabled and it got
+        // no info
+        for (int i = 0; i < actual.length(); i++) {
+            assertFalse(actual.getJSONObject(i).has("importance"));
+        }
+    }
+
+    @Test
+    public void testDumpBansJson_prePermissionMigration() throws Exception {
+        // confirm that the package bans that are in json are only from package preferences, and
+        // not from the passed-in permissions map
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // package preferences: only PKG_P is banned
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // make sure that's the only thing in the package ban output
+        JSONArray actual = mHelper.dumpBansJson(
+                new NotificationManagerService.DumpFilter(), appPermissions);
+        assertThat(actual.length()).isEqualTo(1);
+
+        JSONObject ban = actual.getJSONObject(0);
+        assertThat(ban.getInt("userId")).isEqualTo(UserHandle.getUserId(UID_P));
+        assertThat(ban.getString("packageName")).isEqualTo(PKG_P);
+    }
+
+    @Test
+    public void testDumpBansJson_postPermissionMigration() throws Exception {
+        // confirm that the package bans that are in the output include all packages that
+        // have their permission set to false, and not based on PackagePreferences importance
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // package preferences: PKG_O not banned based on local importance, and PKG_P is
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // expected output
+        ArraySet<Pair<Integer, String>> expected = new ArraySet<>();
+        expected.add(new Pair(UserHandle.getUserId(3), "third"));
+        expected.add(new Pair(UserHandle.getUserId(UID_O), PKG_O));
+
+        // make sure that's the only thing in the package ban output
+        JSONArray actual = mHelper.dumpBansJson(
+                new NotificationManagerService.DumpFilter(), appPermissions);
+        assertThat(actual.length()).isEqualTo(expected.size());
+
+        for (int i = 0; i < actual.length(); i++) {
+            JSONObject ban = actual.getJSONObject(i);
+            assertTrue(expected.contains(
+                    new Pair(ban.getInt("userId"), ban.getString("packageName"))));
+        }
+    }
+
+    @Test
+    public void testDumpBansJson_givenNullInput() throws Exception {
+        // no one should do this, but...
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        JSONArray actual = mHelper.dumpBansJson(
+                new NotificationManagerService.DumpFilter(), null);
+        assertThat(actual.length()).isEqualTo(0);
+    }
+
+    @Test
+    public void testDumpString_prePermissionMigration() {
+        // confirm that the string resulting from dumpImpl contains only info from package prefs
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // local package preferences: PKG_O is not banned even though the permissions would
+        // indicate so
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // get dump output as a string so we can inspect the contents later
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), appPermissions);
+        pw.flush();
+        String actual = sw.toString();
+
+        // expected (substring) output for each preference
+        ArrayList<String> expected = new ArrayList<>();
+        expected.add(PKG_O + " (" + UID_O + ") importance=HIGH");
+        expected.add(PKG_P + " (" + UID_P + ") importance=NONE");
+
+        // make sure the things in app permissions do NOT show up
+        ArrayList<String> notExpected = new ArrayList<>();
+        notExpected.add("first (1) importance=DEFAULT");
+        notExpected.add("third (3) importance=NONE");
+
+        for (String exp : expected) {
+            assertTrue(actual.contains(exp));
+        }
+
+        for (String notExp : notExpected) {
+            assertFalse(actual.contains(notExp));
+        }
+
+        // also make sure it works the same if we pass in a null input
+        StringWriter sw2 = new StringWriter();
+        PrintWriter pw2 = new PrintWriter(sw2);
+        mHelper.dump(pw2, "", new NotificationManagerService.DumpFilter(), null);
+        pw.flush();
+        String actualWithNullInput = sw2.toString();
+        assertThat(actualWithNullInput).isEqualTo(actual);
+    }
+
+    @Test
+    public void testDumpString_postPermissionMigration() {
+        // confirm that the string resulting from dumpImpl contains only importances from permission
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // local package preferences
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // get dump output as a string so we can inspect the contents later
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), appPermissions);
+        pw.flush();
+        String actual = sw.toString();
+
+        // expected (substring) output for each preference via permissions
+        ArrayList<String> expected = new ArrayList<>();
+        expected.add("first (1) importance=DEFAULT");
+        expected.add("third (3) importance=NONE");
+        expected.add(PKG_O + " (" + UID_O + ") importance=NONE");
+        expected.add(PKG_P + " (" + UID_P + ")");
+
+        // make sure we don't have package preference info
+        ArrayList<String> notExpected = new ArrayList<>();
+        notExpected.add(PKG_O + " (" + UID_O + ") importance=HIGH");
+        notExpected.add(PKG_P + " (" + UID_P + ") importance=");  // no importance for PKG_P
+
+        for (String exp : expected) {
+            assertTrue(actual.contains(exp));
+        }
+
+        for (String notExp : notExpected) {
+            assertFalse(actual.contains(notExp));
+        }
+    }
+
+    @Test
+    public void testDumpString_givenNullInput() {
+        // test that this doesn't choke on null input
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+        // local package preferences
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // get dump output
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), null);
+        pw.flush();
+        String actual = sw.toString();
+
+        // nobody gets any importance
+        assertFalse(actual.contains("importance="));
+    }
+
+    @Test
+    public void testDumpProto_prePermissionMigration() throws Exception {
+        // test that dumping to proto gets the importances from the right place
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // local package preferences
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // expected output: only the local preferences
+        // map format: (uid, package name) -> importance (int)
+        ArrayMap<Pair<Integer, String>, Integer> expected = new ArrayMap<>();
+        expected.put(new Pair(UID_O, PKG_O), IMPORTANCE_HIGH);
+        expected.put(new Pair(UID_P, PKG_P), IMPORTANCE_NONE);
+
+        // get the proto output and inspect its contents
+        ProtoOutputStream proto = new ProtoOutputStream();
+        mHelper.dump(proto, new NotificationManagerService.DumpFilter(), appPermissions);
+
+        RankingHelperProto actual = RankingHelperProto.parseFrom(proto.getBytes());
+        assertThat(actual.records.length).isEqualTo(expected.size());
+        for (int i = 0; i < actual.records.length; i++) {
+            RankingHelperProto.RecordProto record = actual.records[i];
+            Pair<Integer, String> pkgKey = new Pair(record.uid, record.package_);
+            assertTrue(expected.containsKey(pkgKey));
+            assertThat(record.importance).isEqualTo(expected.get(pkgKey));
+        }
+
+        // also check that it's the same as passing in null input
+        ProtoOutputStream proto2 = new ProtoOutputStream();
+        mHelper.dump(proto2, new NotificationManagerService.DumpFilter(), null);
+        assertThat(proto.getBytes()).isEqualTo(proto2.getBytes());
+    }
+
+    @Test
+    public void testDumpProto_postPermissionMigration() throws Exception {
+        // test that dumping to proto gets the importances from the right place
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+        // permissions -- these should take precedence
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // local package preferences
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
+
+        // expected output: all the packages, but only the ones provided via appPermissions
+        // should have importance set (aka not PKG_P)
+        // map format: (uid, package name) -> importance (int)
+        ArrayMap<Pair<Integer, String>, Integer> expected = new ArrayMap<>();
+        expected.put(new Pair(1, "first"), IMPORTANCE_DEFAULT);
+        expected.put(new Pair(3, "third"), IMPORTANCE_NONE);
+        expected.put(new Pair(UID_O, PKG_O), IMPORTANCE_NONE);
+
+        // unfortunately, due to how nano protos work, there's no distinction between unset
+        // fields and default-value fields, so we have no choice here but to check for a value of 0.
+        // at least we can make sure the local importance for PKG_P in this test is not 0 (NONE).
+        expected.put(new Pair(UID_P, PKG_P), 0);
+
+        // get the proto output and inspect its contents
+        ProtoOutputStream proto = new ProtoOutputStream();
+        mHelper.dump(proto, new NotificationManagerService.DumpFilter(), appPermissions);
+
+        RankingHelperProto actual = RankingHelperProto.parseFrom(proto.getBytes());
+        assertThat(actual.records.length).isEqualTo(expected.size());
+        for (int i = 0; i < actual.records.length; i++) {
+            RankingHelperProto.RecordProto record = actual.records[i];
+            Pair<Integer, String> pkgKey = new Pair(record.uid, record.package_);
+            assertTrue(expected.containsKey(pkgKey));
+            assertThat(record.importance).isEqualTo(expected.get(pkgKey));
+        }
+    }
+
+    @Test
     public void testBadgingOverrideTrue() throws Exception {
         Secure.putIntForUser(getContext().getContentResolver(),
                 Secure.NOTIFICATION_BADGING, 1,
@@ -4576,6 +4989,86 @@
     }
 
     @Test
+    public void testPullPackagePreferencesStats_prePermissionMigration() {
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+        // build a collection of app permissions that should be passed in but ignored
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // package preferences: PKG_O not banned based on local importance, and PKG_P is
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // expected output. format: uid -> importance, as only uid (and not package name)
+        // is in PackageNotificationPreferences
+        ArrayMap<Integer, Integer> expected = new ArrayMap<>();
+        expected.put(UID_O, IMPORTANCE_HIGH);
+        expected.put(UID_P, IMPORTANCE_NONE);
+
+        // unexpected output. these UIDs should not show up in the output at all
+        ArraySet<Integer> unexpected = new ArraySet<>();
+        unexpected.add(1);
+        unexpected.add(3);
+
+        ArrayList<StatsEvent> events = new ArrayList<>();
+        mHelper.pullPackagePreferencesStats(events, appPermissions);
+
+        for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+            if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
+                int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
+
+                // this shouldn't be any of the forbidden uids
+                assertFalse(unexpected.contains(uid));
+
+                // if it's one of the expected ids, then make sure the importance matches
+                assertTrue(expected.containsKey(uid));
+                assertThat(expected.get(uid)).isEqualTo(
+                            builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+            }
+        }
+    }
+
+    @Test
+    public void testPullPackagePreferencesStats_postPermissionMigration() {
+        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+        // build a collection of app permissions that should be passed in but ignored
+        ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(1, "first"), true);    // not in local prefs
+        appPermissions.put(new Pair(3, "third"), false);   // not in local prefs
+        appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+        // package preferences: PKG_O not banned based on local importance, and PKG_P is
+        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+        // expected output. format: uid -> importance, as only uid (and not package name)
+        // is in PackageNotificationPreferences
+        ArrayMap<Integer, Integer> expected = new ArrayMap<>();
+        expected.put(1, IMPORTANCE_DEFAULT);
+        expected.put(3, IMPORTANCE_NONE);
+        expected.put(UID_O, IMPORTANCE_NONE);    // banned by permissions
+        expected.put(UID_P, IMPORTANCE_NONE);    // defaults to none
+
+        ArrayList<StatsEvent> events = new ArrayList<>();
+        mHelper.pullPackagePreferencesStats(events, appPermissions);
+
+        for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+            if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
+                int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
+
+                // if it's one of the expected ids, then make sure the importance matches
+                assertTrue(expected.containsKey(uid));
+                assertThat(expected.get(uid)).isEqualTo(
+                        builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+            }
+        }
+    }
+
+    @Test
     public void testUnlockNotificationChannelImportance() {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
         mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
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 d4991fc..2954d78 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1084,6 +1084,7 @@
 
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1115,6 +1116,7 @@
 
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
         // In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1147,6 +1149,7 @@
 
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
         setUpDisplaySizeWithApp(1000, 1200);
@@ -1175,6 +1178,52 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+    public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
+        setUpDisplaySizeWithApp(1000, 1200);
+
+        // Create a size compat activity on the same task.
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        // The per-package override forces the activity into a 3:2 aspect ratio
+        assertEquals(1200, activity.getBounds().height());
+        assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+                activity.getBounds().width(), 0.5);
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+    public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
+        // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+        // isn't applied.
+
+        setUpDisplaySizeWithApp(1000, 1200);
+
+        // Create a size compat activity on the same task.
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+
+        // The per-package override forces the activity into a 3:2 aspect ratio
+        assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+                activity.getBounds().height(), 0.5);
+        assertEquals(1000, activity.getBounds().width());
+    }
+
+    @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
     public void testOverrideMinAspectRatioWithoutGlobalOverride() {
         // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index cac716e..0ddd52d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -69,7 +69,14 @@
  * them know that the app has crashed and that their call was continued using the pre-loaded dialer
  * app.
  * <p>
- * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call.
+ * The pre-loaded dialer will ALWAYS be used when the user places an emergency call, even if your
+ * app fills the {@link android.app.role.RoleManager#ROLE_DIALER} role.  To ensure an optimal
+ * experience when placing an emergency call, the default dialer should ALWAYS use
+ * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls (including
+ * emergency calls).  This ensures that the platform is able to verify that the request came from
+ * the default dialer.  If a non-preloaded dialer app uses {@link Intent#ACTION_CALL} to place an
+ * emergency call, it will be raised to the preloaded dialer app using {@link Intent#ACTION_DIAL}
+ * for confirmation; this is a suboptimal user experience.
  * <p>
  * Below is an example manifest registration for an {@code InCallService}. The meta-data
  * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index ae597e0..2b355ae 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -16,11 +16,14 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.RadioAccessSpecifier;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -32,7 +35,6 @@
  * Network Service when passed through {@link TelephonyManager#updateAvailableNetworks}
  */
 public final class AvailableNetworkInfo implements Parcelable {
-
     /*
      * Defines number of priority level high.
      */
@@ -48,6 +50,14 @@
      */
     public static final int PRIORITY_LOW = 3;
 
+    /** @hide */
+    @IntDef({
+        PRIORITY_HIGH,
+        PRIORITY_MED,
+        PRIORITY_LOW,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AvailableNetworkInfoPriority {}
     /**
      * subscription Id of the available network. This value must be one of the entry retrieved from
      * {@link SubscriptionManager#getOpportunisticSubscriptions}
@@ -62,7 +72,7 @@
      * for network selection. If there are more than one subId with highest priority then the
      * network with highest RSRP is chosen.
      */
-    private int mPriority;
+    private @AvailableNetworkInfoPriority int mPriority;
 
     /**
      * Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
@@ -77,8 +87,7 @@
      * Opportunistic network service will use these bands to scan.
      *
      * When no specific bands are specified (empty array or null) CBRS band
-     * {@link AccessNetworkConstants.EutranBand.BAND_48
-     * } will be used for network scan.
+     * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
      *
      * See {@link AccessNetworkConstants} for details.
      *
@@ -94,7 +103,7 @@
      * If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s
      * of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
      * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
-     * by Opportunistic network service.
+     * by Opportunistic network service for a network scan.
      */
     private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers;
 
@@ -117,6 +126,7 @@
      * network with highest RSRP is chosen.
      * @return priority level
      */
+    @AvailableNetworkInfoPriority
     public int getPriority() {
         return mPriority;
     }
@@ -149,15 +159,9 @@
      * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
      * Opportunistic network service will use this to determine which bands to scan for.
      *
-     * the returned value is one of {@link AccessNetworkConstants.AccessNetworkType}. When no
-     * specific access network type is specified, {@link RadioAccessSpecifier}s with {@link
-     * AccessNetworkType}s of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
-     * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
-     * by Opportunistic network service.
      * @return the access network type associated with the available network.
-     * @hide
      */
-    public List<RadioAccessSpecifier>  getRadioAccessSpecifiers() {
+    public @NonNull List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
         return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone();
     }
 
@@ -193,9 +197,9 @@
     }
 
     /** @hide */
-    private AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
-            @NonNull List<Integer> bands, @NonNull List<RadioAccessSpecifier>
-            radioAccessSpecifiers) {
+    private AvailableNetworkInfo(int subId, @AvailableNetworkInfoPriority int priority,
+            @NonNull List<String> mccMncs, @NonNull List<Integer> bands,
+            @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
         mSubId = subId;
         mPriority = priority;
         mMccMncs = new ArrayList<String>(mccMncs);
@@ -261,27 +265,39 @@
      *
      * <pre><code>
      *
-     * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder()
-     *     .setSubId(1)
+     * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder(subId)
      *     .setPriority(AvailableNetworkInfo.PRIORITY_MED)
+     *     .setRadioAccessSpecifiers(radioAccessSpecifiers)
+     *     .setMccMncs(mccMncs)
      *     .build();
      * </code></pre>
-     *
-     * @hide
      */
     public static final class Builder {
         private int mSubId = Integer.MIN_VALUE;
-        private int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
+        private @AvailableNetworkInfoPriority int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
         private ArrayList<String> mMccMncs = new ArrayList<>();
-        private ArrayList<Integer> mBands = new ArrayList<>();
         private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>();
 
-        public @NonNull Builder setSubId(int subId) {
+        /**
+         *
+         */
+        /**
+         * Creates an AvailableNetworkInfo Builder with specified subscription id.
+         *
+         * @param subId of the availableNetwork.
+         */
+        public Builder(int subId) {
             mSubId = subId;
-            return this;
         }
 
-        public @NonNull Builder setPriority(int priority) {
+        /**
+         * Sets the priority for the subscription id.
+         *
+         * @param priority of the subscription id. See {@link AvailableNetworkInfo#getPriority} for
+         * more details
+         * @return the original Builder object.
+         */
+        public @NonNull Builder setPriority(@AvailableNetworkInfoPriority int priority) {
             if (priority > AvailableNetworkInfo.PRIORITY_LOW
                     || priority < AvailableNetworkInfo.PRIORITY_HIGH) {
                 throw new IllegalArgumentException("A valid priority must be set");
@@ -290,30 +306,48 @@
             return this;
         }
 
-        public @NonNull Builder setMccMncs(@NonNull ArrayList<String> mccMncs) {
-            Objects.requireNonNull(mccMncs, "A non-null ArrayList of mccmncs must be set. An empty "
-                    + "list is still accepted. Please read documentation in "
-                    + "AvailableNetworkService to see consequences of an empty Arraylist.");
-            mMccMncs = mccMncs;
+        /**
+         * Sets the list of mccmncs associated with the subscription id.
+         *
+         * @param mccMncs nonull list of mccmncs. An empty List is still accepted. Please read
+         * documentation in {@link AvailableNetworkInfo} to see consequences of an empty List.
+         * @return the original Builder object.
+         */
+        public @NonNull Builder setMccMncs(@NonNull List<String> mccMncs) {
+            Objects.requireNonNull(mccMncs, "A non-null List of mccmncs must be set. An empty "
+                    + "List is still accepted. Please read documentation in "
+                    + "AvailableNetworkInfo to see consequences of an empty List.");
+            mMccMncs = new ArrayList<>(mccMncs);
             return this;
         }
 
+        /**
+         * Sets the list of mccmncs associated with the subscription id.
+         *
+         * @param radioAccessSpecifiers nonull list of radioAccessSpecifiers. An empty List is still
+         * accepted. Please read documentation in {@link AvailableNetworkInfo} to see
+         * consequences of an empty List.
+         * @return the original Builder object.
+         */
         public @NonNull Builder setRadioAccessSpecifiers(
-                @NonNull ArrayList<RadioAccessSpecifier> radioAccessSpecifiers) {
-            Objects.requireNonNull(radioAccessSpecifiers, "A non-null ArrayList of "
-                    + "RadioAccessSpecifiers must be set. An empty list is still accepted. Please "
-                    + "read documentation in AvailableNetworkService to see consequences of an "
-                    + "empty Arraylist.");
-            mRadioAccessSpecifiers = radioAccessSpecifiers;
+                @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
+            Objects.requireNonNull(radioAccessSpecifiers, "A non-null List of "
+                    + "RadioAccessSpecifiers must be set. An empty List is still accepted. Please "
+                    + "read documentation in AvailableNetworkInfo to see consequences of an "
+                    + "empty List.");
+            mRadioAccessSpecifiers = new ArrayList<>(radioAccessSpecifiers);
             return this;
         }
 
+        /**
+         * @return an AvailableNetworkInfo object with all the fields previously set by the Builder.
+         */
         public @NonNull AvailableNetworkInfo build() {
             if (mSubId == Integer.MIN_VALUE) {
                 throw new IllegalArgumentException("A valid subId must be set");
             }
 
-            return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, mBands,
+            return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, new ArrayList<>(),
                     mRadioAccessSpecifiers);
         }
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3e9ae38..2dfa9a45 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3697,6 +3697,49 @@
             "show_wifi_calling_icon_in_status_bar_bool";
 
     /**
+     * Configuration to indicate that the carrier supports opportunistic data
+     * auto provisioning. Based on this flag, the device downloads and activates
+     * corresponding opportunistic profile.
+     */
+    public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL =
+            "carrier_supports_opp_data_auto_provisioning_bool";
+
+    /**
+     * SMDP+ server address for downloading opportunistic eSIM profile.
+     * FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
+     * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 [15] excluding '$'.
+     */
+    public static final String KEY_SMDP_SERVER_ADDRESS_STRING =
+            "smdp_server_address_string";
+
+    /**
+     * This timer value is used in the eSIM Exponential Backoff download retry algorithm.
+     * Value should be in seconds.
+     * <OL>
+     *     <LI>When the first download failure occurs, retry download after BACKOFF_TIMER_VALUE
+     * seconds.</LI>
+     *
+     * <LI>If download fails again then, retry after either BACKOFF_TIMER_VALUE,
+     * 2xBACKOFF_TIMER_VALUE, or 3xBACKOFF_TIMER_VALUE seconds.</LI>
+     *
+     * <LI>In general after the cth failed attempt, retry after k * BACKOFF_TIMER_VALUE
+     * seconds, where k is a random integer between 1 and 2^c − 1. Max c value is
+     * {@link #KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT}</LI>
+     * </OL>
+     */
+    public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT =
+            "esim_download_retry_backoff_timer_sec_int";
+
+    /**
+     * If eSIM profile download fails then, the number of retry attempts by UE
+     * will be based on this configuration. If download still fails even after the
+     * MAX attempts configured by this item then the retry is postponed until next
+     * device bootup.
+     */
+    public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT =
+            "esim_max_download_retry_attempts_int";
+
+    /**
      * Controls RSRP threshold at which OpportunisticNetworkService will decide whether
      * the opportunistic network is good enough for internet data.
      */
@@ -3906,6 +3949,30 @@
     public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
             "enabled_4g_opportunistic_network_scan_bool";
 
+  /**
+   * Only relevant when the device supports opportunistic networks but does not support
+   * simultaneuous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
+   * goes out of service before switching the 5G capability back to primary stack. The idea of
+   * waiting a few seconds is to minimize the calling of the expensive capability switching
+   * operation in the case where CBRS goes back into service shortly after going out of it.
+   *
+   * @hide
+   */
+  public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
+            "time_to_switch_back_to_primary_if_opportunistic_oos_long";
+
+  /**
+   * Only relevant when the device supports opportunistic networks but does not support
+   * simultaneuous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
+   * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
+   * 'ping-ponging' effect where device is constantly witching capability back and forth between
+   * primary and opportunistic stack.
+   *
+   * @hide
+   */
+  public static final String KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG
+          = "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
+
     /**
      * Indicates zero or more emergency number prefix(es), because some carrier requires
      * if users dial an emergency number address with a specific prefix, the combination of the
@@ -5781,6 +5848,10 @@
         sDefaults.putBoolean(KEY_UNMETERED_NR_SA_SUB6_BOOL, false);
         sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL, false);
+        sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
+        sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
+        sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
@@ -5824,6 +5895,10 @@
         /* Default value is 2 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
         sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
+        sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
+        sDefaults.putInt(
+                KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
+                120000);
         sDefaults.putAll(Gps.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
                 new int[] {
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5d2eda3..a266b47 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -154,7 +154,7 @@
   const char* end = path.end();
   const char* last_dir_sep = path.begin();
   for (const char* c = path.begin(); c != end; ++c) {
-    if (*c == sDirSep) {
+    if (*c == sDirSep || *c == sInvariantDirSep) {
       last_dir_sep = c + 1;
     }
   }
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 877cd56..a2b1b58 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -41,6 +41,8 @@
 constexpr const char sPathSep = ':';
 #endif
 
+constexpr const char sInvariantDirSep = '/';
+
 enum class FileType {
   kUnknown = 0,
   kNonExistant,