Merge "Update Layout.Builder APIs" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 16907b3..b753aab 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -19,6 +19,7 @@
     // Add java_aconfig_libraries to here to add them to the core framework
     srcs: [
         ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}",
+        ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
     ],
 }
 
@@ -30,6 +31,7 @@
     libs: ["fake_device_config"],
 }
 
+// Camera
 aconfig_declarations {
     name: "com.android.hardware.camera2-aconfig",
     package: "com.android.hardware.camera2",
@@ -41,3 +43,16 @@
     aconfig_declarations: "com.android.hardware.camera2-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Window
+aconfig_declarations {
+    name: "com.android.window.flags.window-aconfig",
+    package: "com.android.window.flags",
+    srcs: ["core/java/android/window/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "com.android.window.flags.window-aconfig-java",
+    aconfig_declarations: "com.android.window.flags.window-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 53554bc..431f0b9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -408,21 +408,18 @@
     ],
 }
 
-java_library {
-    name: "framework-minus-apex",
+// Separated so framework-minus-apex-defaults can be used without the libs dependency
+java_defaults {
+    name: "framework-minus-apex-with-libs-defaults",
     defaults: ["framework-minus-apex-defaults"],
-    installable: true,
-    // For backwards compatibility.
-    stem: "framework",
-    apex_available: ["//apex_available:platform"],
-    visibility: [
-        "//frameworks/base",
-        // TODO(b/147128803) remove the below lines
-        "//frameworks/base/apex/blobstore/framework",
-        "//frameworks/base/apex/jobscheduler/framework",
-        "//frameworks/base/packages/Tethering/tests/unit",
-        "//packages/modules/Connectivity/Tethering/tests/unit",
+    libs: [
+        "framework-virtualization.stubs.module_lib",
+        "framework-location.impl",
     ],
+}
+
+java_defaults {
+    name: "framework-non-updatable-lint-defaults",
     lint: {
         extra_check_modules: ["AndroidFrameworkLintChecker"],
         disabled_checks: ["ApiMightLeakAppVisibility"],
@@ -436,6 +433,43 @@
             "UseOfCallerAwareMethodsWithClearedIdentity",
         ],
     },
+}
+
+// we are unfortunately building the turbine jar twice, but more efficient and less complex
+// than generating a similar set of stubs with metalava
+java_library {
+    name: "framework-minus-apex-headers",
+    defaults: ["framework-minus-apex-defaults"],
+    installable: false,
+    // For backwards compatibility.
+    stem: "framework",
+    apex_available: ["//apex_available:platform"],
+    visibility: [
+        "//frameworks/base/location",
+    ],
+    compile_dex: false,
+    headers_only: true,
+}
+
+java_library {
+    name: "framework-minus-apex",
+    defaults: [
+        "framework-minus-apex-with-libs-defaults",
+        "framework-non-updatable-lint-defaults",
+    ],
+    installable: true,
+    // For backwards compatibility.
+    stem: "framework",
+    apex_available: ["//apex_available:platform"],
+    visibility: [
+        "//frameworks/base",
+        "//frameworks/base/location",
+        // TODO(b/147128803) remove the below lines
+        "//frameworks/base/apex/blobstore/framework",
+        "//frameworks/base/apex/jobscheduler/framework",
+        "//frameworks/base/packages/Tethering/tests/unit",
+        "//packages/modules/Connectivity/Tethering/tests/unit",
+    ],
     errorprone: {
         javacflags: [
             "-Xep:AndroidFrameworkCompatChange:ERROR",
@@ -446,7 +480,7 @@
 
 java_library {
     name: "framework-minus-apex-intdefs",
-    defaults: ["framework-minus-apex-defaults"],
+    defaults: ["framework-minus-apex-with-libs-defaults"],
     plugins: ["intdef-annotation-processor"],
 
     // Errorprone and android lint will already run on framework-minus-apex, don't rerun them on
@@ -474,6 +508,7 @@
     installable: false, // this lib is a build-only library
     static_libs: [
         "app-compat-annotations",
+        "framework-location.impl",
         "framework-minus-apex",
         "framework-updatable-stubs-module_libs_api",
     ],
@@ -701,6 +736,97 @@
     ],
 }
 
+// Defaults for the java_sdk_libraries of unbundled jars from framework.
+// java_sdk_libraries using these defaults should also add themselves to the
+// non_updatable_modules list in frameworks/base/api/api.go
+java_defaults {
+    name: "framework-non-updatable-unbundled-defaults",
+    defaults: ["framework-non-updatable-lint-defaults"],
+
+    sdk_version: "core_platform",
+
+    // Api scope settings
+    public: {
+        enabled: true,
+        sdk_version: "module_current",
+        libs: ["android_module_lib_stubs_current"],
+    },
+    system: {
+        enabled: true,
+        sdk_version: "module_current",
+        libs: ["android_module_lib_stubs_current"],
+    },
+    module_lib: {
+        enabled: true,
+        sdk_version: "module_current",
+        libs: ["android_module_lib_stubs_current"],
+    },
+    test: {
+        enabled: true,
+        sdk_version: "test_frameworks_core_current",
+        libs: ["android_test_frameworks_core_stubs_current"],
+    },
+
+    stub_only_libs: [
+        "framework-protos",
+    ],
+    impl_only_libs: [
+        "framework-minus-apex-headers", // full access to framework-minus-apex including hidden API
+        "framework-annotations-lib",
+    ],
+    visibility: ["//visibility:public"],
+    stubs_library_visibility: ["//visibility:public"],
+    stubs_source_visibility: ["//visibility:private"],
+    impl_library_visibility: [
+        ":__pkg__",
+        "//frameworks/base",
+        "//frameworks/base/api", // For framework-all
+    ],
+    defaults_visibility: [
+        "//frameworks/base/location",
+    ],
+    plugins: [
+        "error_prone_android_framework",
+    ],
+    errorprone: {
+        javacflags: [
+            "-Xep:AndroidFrameworkCompatChange:ERROR",
+            "-Xep:AndroidFrameworkUid:ERROR",
+        ],
+    },
+
+    // Include manual annotations in API txt files
+    merge_annotations_dirs: ["metalava-manual"],
+
+    // Use the source of annotations that affect metalava doc generation, since
+    // the relevant generation instructions are themselves in javadoc, which is
+    // not present in class files.
+    api_srcs: [":framework-metalava-annotations"],
+
+    // Framework modules are not generally shared libraries, i.e. they are not
+    // intended, and must not be allowed, to be used in a <uses-library> manifest
+    // entry.
+    shared_library: false,
+
+    // Prevent dependencies that do not specify an sdk_version from accessing the
+    // implementation library by default and force them to use stubs instead.
+    default_to_stubs: true,
+
+    // Subdirectory for the artifacts that are copied to the dist directory
+    dist_group: "android",
+
+    droiddoc_options: [
+        "--error UnhiddenSystemApi " +
+            "--hide CallbackInterface " +
+            "--hide HiddenTypedefConstant " +
+            "--hide RequiresPermission " +
+            "--enhance-documentation " +
+            "--hide-package com.android.server ",
+    ],
+
+    annotations_enabled: true,
+}
+
 build = [
     "AconfigFlags.bp",
     "ProtoLibraries.bp",
diff --git a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
index 9482591..aadbc23 100644
--- a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
+++ b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
@@ -130,7 +130,7 @@
                 eventTime, ACTION_MOVE, /*x=*/eventPosition, /*y=*/eventPosition)
             predictor.record(moveEvent)
             val predictionTime = eventTime + eventInterval
-            val predicted = predictor.predict(predictionTime.toNanos())
+            val predicted = checkNotNull(predictor.predict(predictionTime.toNanos()))
             assertTrue(predicted.eventTime <= (predictionTime + offset).toMillis())
         }
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 109686d..e636f60 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -456,6 +456,7 @@
                 if (DEBUG) {
                     Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
                 }
+                mContext.unbindService(this);
                 mRunningJob = null;
                 mRunningJobWorkType = WORK_TYPE_NONE;
                 mRunningCallback = null;
diff --git a/api/Android.bp b/api/Android.bp
index e9cc405..6986ac0 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -87,6 +87,7 @@
         "framework-devicelock",
         "framework-graphics",
         "framework-healthfitness",
+        "framework-location",
         "framework-media",
         "framework-mediaprovider",
         "framework-ondevicepersonalization",
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index c37ff97..e481fe9 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -487,7 +487,6 @@
     static_libs: [
         "all-updatable-modules-system-stubs",
         "android-non-updatable.stubs.test",
-        "private-stub-annotations-jar",
     ],
     defaults: [
         "android.jar_defaults",
@@ -613,6 +612,7 @@
     api_contributions: [
         "test-api-stubs-docs-non-updatable.api.contribution",
         "framework-virtualization.stubs.source.test.api.contribution",
+        "framework-location.stubs.source.test.api.contribution",
     ],
 }
 
diff --git a/api/api.go b/api/api.go
index d5c6145..6095a9a 100644
--- a/api/api.go
+++ b/api/api.go
@@ -31,6 +31,7 @@
 const conscrypt = "conscrypt.module.public.api"
 const i18n = "i18n.module.public.api"
 const virtualization = "framework-virtualization"
+const location = "framework-location"
 
 var core_libraries_modules = []string{art, conscrypt, i18n}
 
@@ -42,7 +43,7 @@
 // APIs.
 // In addition, the modules in this list are allowed to contribute to test APIs
 // stubs.
-var non_updatable_modules = []string{virtualization}
+var non_updatable_modules = []string{virtualization, location}
 
 // The intention behind this soong plugin is to generate a number of "merged"
 // API-related modules that would otherwise require a large amount of very
@@ -278,8 +279,10 @@
 }
 
 func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
-	// The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
+	// The user of this module compiles against the "core" SDK and against non-updatable modules,
+	// so remove to avoid dupes.
 	modules = removeAll(modules, core_libraries_modules)
+	modules = removeAll(modules, non_updatable_modules)
 	props := libraryProps{}
 	props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
 	props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 3534624..98767ee 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -74,4 +74,7 @@
         "libGLESv2",
         "libgui",
     ],
+    whole_static_libs: [
+        "libc++fs",
+    ],
 }
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 774ba74..b56dceb 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "BootAnimation"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <filesystem>
 #include <vector>
 
 #include <stdint.h>
@@ -1306,10 +1307,10 @@
             continue;
         }
 
-        const String8 entryName(name);
-        const String8 path(entryName.getPathDir());
-        const String8 leaf(entryName.getPathLeaf());
-        if (leaf.size() > 0) {
+        const std::filesystem::path entryName(name);
+        const std::filesystem::path path(entryName.parent_path());
+        const std::filesystem::path leaf(entryName.filename());
+        if (!leaf.empty()) {
             if (entryName == CLOCK_FONT_ZIP_NAME) {
                 FileMap* map = zip->createEntryFileMap(entry);
                 if (map) {
@@ -1327,7 +1328,7 @@
             }
 
             for (size_t j = 0; j < pcount; j++) {
-                if (path == animation.parts[j].path) {
+                if (path.string() == animation.parts[j].path.c_str()) {
                     uint16_t method;
                     // supports only stored png files
                     if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) {
@@ -1344,7 +1345,7 @@
                                                         map->getDataLength());
                                 } else {
                                     Animation::Frame frame;
-                                    frame.name = leaf;
+                                    frame.name = leaf.c_str();
                                     frame.map = map;
                                     frame.trimWidth = animation.width;
                                     frame.trimHeight = animation.height;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6141583..2b3e8e9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -301,6 +301,7 @@
     field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
     field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
     field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
+    field public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
     field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
     field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
     field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
@@ -17233,9 +17234,8 @@
     method public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
   }
 
-  public class SatelliteManager {
+  public final class SatelliteManager {
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void onDeviceAlignedWithSatellite(boolean);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
@@ -17250,6 +17250,7 @@
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendSatelliteDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3927b40..3abe1d9 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -24,6 +24,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.UiThread;
 import android.annotation.UserIdInt;
 import android.app.IServiceConnection;
 import android.app.PendingIntent;
@@ -39,6 +40,10 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
@@ -53,6 +58,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 
 /**
  * Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -475,6 +481,8 @@
 
     private static final String TAG = "AppWidgetManager";
 
+    private static Executor sUpdateExecutor;
+
     /**
      * An intent extra that contains multiple appWidgetIds.  These are id values as
      * they were provided to the application during a recent restore from backup.  It is
@@ -510,6 +518,8 @@
     private final IAppWidgetService mService;
     private final DisplayMetrics mDisplayMetrics;
 
+    private boolean mHasPostedLegacyLists = false;
+
     /**
      * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
      * Context} object.
@@ -552,6 +562,13 @@
         });
     }
 
+    private boolean isPostingTaskToBackground(@Nullable RemoteViews views) {
+        return Looper.myLooper() == Looper.getMainLooper()
+                && RemoteViews.isAdapterConversionEnabled()
+                && (mHasPostedLegacyLists = mHasPostedLegacyLists
+                        || (views != null && views.hasLegacyLists()));
+    }
+
     /**
      * Set the RemoteViews to use for the specified appWidgetIds.
      * <p>
@@ -575,6 +592,19 @@
         if (mService == null) {
             return;
         }
+
+        if (isPostingTaskToBackground(views)) {
+            createUpdateExecutorIfNull().execute(() -> {
+                try {
+                    mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error updating app widget views in background", e);
+                }
+            });
+
+            return;
+        }
+
         try {
             mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
         } catch (RemoteException e) {
@@ -683,6 +713,19 @@
         if (mService == null) {
             return;
         }
+
+        if (isPostingTaskToBackground(views)) {
+            createUpdateExecutorIfNull().execute(() -> {
+                try {
+                    mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error partially updating app widget views in background", e);
+                }
+            });
+
+            return;
+        }
+
         try {
             mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
         } catch (RemoteException e) {
@@ -738,6 +781,19 @@
         if (mService == null) {
             return;
         }
+
+        if (isPostingTaskToBackground(views)) {
+            createUpdateExecutorIfNull().execute(() -> {
+                try {
+                    mService.updateAppWidgetProvider(provider, views);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error updating app widget view using provider in background", e);
+                }
+            });
+
+            return;
+        }
+
         try {
             mService.updateAppWidgetProvider(provider, views);
         } catch (RemoteException e) {
@@ -797,28 +853,45 @@
         if (mService == null) {
             return;
         }
-        try {
-            if (RemoteViews.isAdapterConversionEnabled()) {
-                List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
-                for (int i = 0; i < appWidgetIds.length; i++) {
-                    final int widgetId = appWidgetIds[i];
-                    updateFutures.add(CompletableFuture.runAsync(() -> {
-                        try {
-                            RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
-                            if (views.replaceRemoteCollections(viewId)) {
-                                updateAppWidget(widgetId, views);
-                            }
-                        } catch (Exception e) {
-                            Log.e(TAG, "Error notifying changes in RemoteViews", e);
-                        }
-                    }));
-                }
-                CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
-            } else {
+
+        if (!RemoteViews.isAdapterConversionEnabled()) {
+            try {
                 mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
             }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+
+            return;
+        }
+
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            mHasPostedLegacyLists = true;
+            createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(appWidgetIds,
+                    viewId));
+        } else {
+            notifyCollectionWidgetChange(appWidgetIds, viewId);
+        }
+    }
+
+    private void notifyCollectionWidgetChange(int[] appWidgetIds, int viewId) {
+        try {
+            List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
+            for (int i = 0; i < appWidgetIds.length; i++) {
+                final int widgetId = appWidgetIds[i];
+                updateFutures.add(CompletableFuture.runAsync(() -> {
+                    try {
+                        RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
+                        if (views.replaceRemoteCollections(viewId)) {
+                            updateAppWidget(widgetId, views);
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error notifying changes in RemoteViews", e);
+                    }
+                }));
+            }
+            CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
+        } catch (Exception e) {
+            Log.e(TAG, "Error notifying changes for all widgets", e);
         }
     }
 
@@ -1360,4 +1433,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    @UiThread
+    private static @NonNull Executor createUpdateExecutorIfNull() {
+        if (sUpdateExecutor == null) {
+            sUpdateExecutor = new HandlerExecutor(createAndStartNewHandler(
+                    "widget_manager_update_helper_thread", Process.THREAD_PRIORITY_FOREGROUND));
+        }
+
+        return sUpdateExecutor;
+    }
+
+    private static @NonNull Handler createAndStartNewHandler(@NonNull String name, int priority) {
+        HandlerThread thread = new HandlerThread(name, priority);
+        thread.start();
+        return thread.getThreadHandler();
+    }
 }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index ccc39b6..0396443 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -41,11 +41,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RSIllegalArgumentException;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Surface;
@@ -1007,132 +1002,6 @@
     private native final void _addCallbackBuffer(
                                 byte[] callbackBuffer, int msgType);
 
-    /**
-     * <p>Create a {@link android.renderscript RenderScript}
-     * {@link android.renderscript.Allocation Allocation} to use as a
-     * destination of preview callback frames. Use
-     * {@link #setPreviewCallbackAllocation setPreviewCallbackAllocation} to use
-     * the created Allocation as a destination for camera preview frames.</p>
-     *
-     * <p>The Allocation will be created with a YUV type, and its contents must
-     * be accessed within Renderscript with the {@code rsGetElementAtYuv_*}
-     * accessor methods. Its size will be based on the current
-     * {@link Parameters#getPreviewSize preview size} configured for this
-     * camera.</p>
-     *
-     * @param rs the RenderScript context for this Allocation.
-     * @param usage additional usage flags to set for the Allocation. The usage
-     *   flag {@link android.renderscript.Allocation#USAGE_IO_INPUT} will always
-     *   be set on the created Allocation, but additional flags may be provided
-     *   here.
-     * @return a new YUV-type Allocation with dimensions equal to the current
-     *   preview size.
-     * @throws RSIllegalArgumentException if the usage flags are not compatible
-     *   with an YUV Allocation.
-     * @see #setPreviewCallbackAllocation
-     * @hide
-     */
-    public final Allocation createPreviewAllocation(RenderScript rs, int usage)
-            throws RSIllegalArgumentException {
-        Parameters p = getParameters();
-        Size previewSize = p.getPreviewSize();
-        Type.Builder yuvBuilder = new Type.Builder(rs,
-                Element.createPixel(rs,
-                        Element.DataType.UNSIGNED_8,
-                        Element.DataKind.PIXEL_YUV));
-        // Use YV12 for wide compatibility. Changing this requires also
-        // adjusting camera service's format selection.
-        yuvBuilder.setYuvFormat(ImageFormat.YV12);
-        yuvBuilder.setX(previewSize.width);
-        yuvBuilder.setY(previewSize.height);
-
-        Allocation a = Allocation.createTyped(rs, yuvBuilder.create(),
-                usage | Allocation.USAGE_IO_INPUT);
-
-        return a;
-    }
-
-    /**
-     * <p>Set an {@link android.renderscript.Allocation Allocation} as the
-     * target of preview callback data. Use this method for efficient processing
-     * of camera preview data with RenderScript. The Allocation must be created
-     * with the {@link #createPreviewAllocation createPreviewAllocation }
-     * method.</p>
-     *
-     * <p>Setting a preview allocation will disable any active preview callbacks
-     * set by {@link #setPreviewCallback setPreviewCallback} or
-     * {@link #setPreviewCallbackWithBuffer setPreviewCallbackWithBuffer}, and
-     * vice versa. Using a preview allocation still requires an active standard
-     * preview target to be set, either with
-     * {@link #setPreviewTexture setPreviewTexture} or
-     * {@link #setPreviewDisplay setPreviewDisplay}.</p>
-     *
-     * <p>To be notified when new frames are available to the Allocation, use
-     * {@link android.renderscript.Allocation#setIoInputNotificationHandler Allocation.setIoInputNotificationHandler}. To
-     * update the frame currently accessible from the Allocation to the latest
-     * preview frame, call
-     * {@link android.renderscript.Allocation#ioReceive Allocation.ioReceive}.</p>
-     *
-     * <p>To disable preview into the Allocation, call this method with a
-     * {@code null} parameter.</p>
-     *
-     * <p>Once a preview allocation is set, the preview size set by
-     * {@link Parameters#setPreviewSize setPreviewSize} cannot be changed. If
-     * you wish to change the preview size, first remove the preview allocation
-     * by calling {@code setPreviewCallbackAllocation(null)}, then change the
-     * preview size, create a new preview Allocation with
-     * {@link #createPreviewAllocation createPreviewAllocation}, and set it as
-     * the new preview callback allocation target.</p>
-     *
-     * <p>If you are using the preview data to create video or still images,
-     * strongly consider using {@link android.media.MediaActionSound} to
-     * properly indicate image capture or recording start/stop to the user.</p>
-     *
-     * @param previewAllocation the allocation to use as destination for preview
-     * @throws IOException if configuring the camera to use the Allocation for
-     *   preview fails.
-     * @throws IllegalArgumentException if the Allocation's dimensions or other
-     *   parameters don't meet the requirements.
-     * @see #createPreviewAllocation
-     * @see #setPreviewCallback
-     * @see #setPreviewCallbackWithBuffer
-     * @hide
-     */
-    public final void setPreviewCallbackAllocation(Allocation previewAllocation)
-            throws IOException {
-        Surface previewSurface = null;
-        if (previewAllocation != null) {
-             Parameters p = getParameters();
-             Size previewSize = p.getPreviewSize();
-             if (previewSize.width != previewAllocation.getType().getX() ||
-                     previewSize.height != previewAllocation.getType().getY()) {
-                 throw new IllegalArgumentException(
-                     "Allocation dimensions don't match preview dimensions: " +
-                     "Allocation is " +
-                     previewAllocation.getType().getX() +
-                     ", " +
-                     previewAllocation.getType().getY() +
-                     ". Preview is " + previewSize.width + ", " +
-                     previewSize.height);
-             }
-             if ((previewAllocation.getUsage() &
-                             Allocation.USAGE_IO_INPUT) == 0) {
-                 throw new IllegalArgumentException(
-                     "Allocation usage does not include USAGE_IO_INPUT");
-             }
-             if (previewAllocation.getType().getElement().getDataKind() !=
-                     Element.DataKind.PIXEL_YUV) {
-                 throw new IllegalArgumentException(
-                     "Allocation is not of a YUV type");
-             }
-             previewSurface = previewAllocation.getSurface();
-             mUsingPreviewAllocation = true;
-         } else {
-             mUsingPreviewAllocation = false;
-         }
-         setPreviewCallbackSurface(previewSurface);
-    }
-
     private native final void setPreviewCallbackSurface(Surface s);
 
     private class EventHandler extends Handler
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0e45787..082a336 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -541,14 +541,6 @@
      *   or configuring it to use one of the supported
      *   {@link android.media.CamcorderProfile CamcorderProfiles}.</li>
      *
-     * <li>For efficient YUV processing with {@link android.renderscript}:
-     *   Create a RenderScript
-     *   {@link android.renderscript.Allocation Allocation} with a supported YUV
-     *   type, the IO_INPUT flag, and one of the sizes returned by
-     *   {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(Allocation.class)},
-     *   Then obtain the Surface with
-     *   {@link android.renderscript.Allocation#getSurface}.</li>
-     *
      * <li>For access to RAW, uncompressed YUV, or compressed JPEG data in the application: Create an
      *   {@link android.media.ImageReader} object with one of the supported output formats given by
      *   {@link StreamConfigurationMap#getOutputFormats()}, setting its size to one of the
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index ef0db7f..b85d686 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -509,8 +509,6 @@
      * Recommended for recording video (simple to use)
      * <li>{@link android.media.MediaCodec} -
      * Recommended for recording video (more complicated to use, with more flexibility)
-     * <li>{@link android.renderscript.Allocation} -
-     * Recommended for image processing with {@link android.renderscript RenderScript}
      * <li>{@link android.view.SurfaceHolder} -
      * Recommended for low-power camera preview with {@link android.view.SurfaceView}
      * <li>{@link android.graphics.SurfaceTexture} -
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index af09a06..5b24dca 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1294,32 +1294,30 @@
      * Round the given size of a storage device to a nice round power-of-two
      * value, such as 256MB or 32GB. This avoids showing weird values like
      * "29.5GB" in UI.
-     *
-     * Some storage devices are still using GiB (powers of 1024) over
-     * GB (powers of 1000) measurements and this method takes it into account.
-     *
      * Round ranges:
      * ...
-     * [256 GiB + 1; 512 GiB] -> 512 GB
-     * [512 GiB + 1; 1 TiB]   -> 1 TB
-     * [1 TiB + 1; 2 TiB]     -> 2 TB
+     * (128 GB; 256 GB]   -> 256 GB
+     * (256 GB; 512 GB]   -> 512 GB
+     * (512 GB; 1000 GB]  -> 1000 GB
+     * (1000 GB; 2000 GB] -> 2000 GB
+     * ...
      * etc
      *
      * @hide
      */
     public static long roundStorageSize(long size) {
         long val = 1;
-        long kiloPow = 1;
-        long kibiPow = 1;
-        while ((val * kibiPow) < size) {
+        long pow = 1;
+        while ((val * pow) < size) {
             val <<= 1;
             if (val > 512) {
                 val = 1;
-                kibiPow *= 1024;
-                kiloPow *= 1000;
+                pow *= 1000;
             }
         }
-        return val * kiloPow;
+
+        Log.d(TAG, String.format("Rounded bytes from %d to %d", size, val * pow));
+        return val * pow;
     }
 
     private static long toBytes(long value, String unit) {
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bc52744..369a193 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -174,4 +174,5 @@
     boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
     void setCloudMediaProvider(in String authority) = 96;
     String getCloudMediaProvider() = 97;
+    long getInternalStorageBlockDeviceSize() = 98;
 }
\ No newline at end of file
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 80dd488..ee387e7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1359,6 +1359,15 @@
     }
 
     /** {@hide} */
+    public long getInternalStorageBlockDeviceSize() {
+        try {
+            return mStorageManager.getInternalStorageBlockDeviceSize();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
     public void mkdirs(File file) {
         BlockGuard.getVmPolicy().onPathAccess(file.getAbsolutePath());
         try {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2791c41..b9a6583 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4579,6 +4579,16 @@
         public static final String WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE =
                 "wear_accessibility_gesture_enabled_during_oobe";
 
+
+        /**
+         * If the text-to-speech pre-warm is enabled.
+         * Set to 1 for true and 0 for false.
+         *
+         * This setting is used only internally.
+         * @hide
+         */
+        public static final String WEAR_TTS_PREWARM_ENABLED = "wear_tts_prewarm_enabled";
+
         /**
          * @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_ON} instead
          */
@@ -6017,6 +6027,7 @@
             PRIVATE_SETTINGS.add(ADVANCED_SETTINGS);
             PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED);
             PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE);
+            PRIVATE_SETTINGS.add(WEAR_TTS_PREWARM_ENABLED);
             PRIVATE_SETTINGS.add(SCREEN_AUTO_BRIGHTNESS_ADJ);
             PRIVATE_SETTINGS.add(VIBRATE_INPUT_DEVICES);
             PRIVATE_SETTINGS.add(VOLUME_MASTER);
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 4e8d6e7..22b1f02 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -1,9 +1,9 @@
 # Bug component: 36824
 
-cbrubaker@google.com
+brambonne@google.com
+brufino@google.com
+jeffv@google.com
 
-per-file NetworkSecurityPolicy.java = cbrubaker@google.com
-per-file NetworkSecurityPolicy.java = klyubin@google.com
-per-file FrameworkNetworkSecurityPolicy.java = cbrubaker@google.com
-per-file FrameworkNetworkSecurityPolicy.java = klyubin@google.com
+per-file *NetworkSecurityPolicy.java = file:net/OWNERS
 per-file Confirmation*.java = file:/keystore/OWNERS
+per-file FileIntegrityManager.java = victorhsieh@google.com
diff --git a/core/java/android/security/net/OWNERS b/core/java/android/security/net/OWNERS
index d828164..1d52eed 100644
--- a/core/java/android/security/net/OWNERS
+++ b/core/java/android/security/net/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 36824
 
-cbrubaker@google.com
 brambonne@google.com
+jeffv@google.com
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 827600c..85f5395 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -247,6 +247,7 @@
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true");
         DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true");
         DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false");
+        DEFAULT_FLAGS.put("settings_press_hold_nav_handle_to_search", "false");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index 511cb2d..ac76fc2 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -212,6 +212,11 @@
         return -1;
     }
 
+    /** Returns {@code true} if this array contains the specified value. */
+    public boolean contains(int value) {
+        return indexOf(value) != -1;
+    }
+
     /**
      * Removes the value at the specified index from this array.
      */
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 6e73a3c..23afb03 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -346,13 +346,13 @@
     }
 
     private static boolean shouldTriggerStylusHandwritingForView(@NonNull View view) {
-        if (!view.isAutoHandwritingEnabled()) {
+        if (!view.shouldInitiateHandwriting()) {
             return false;
         }
-        // The view may be a handwriting initiation delegate, in which case it is not the editor
+        // The view may be a handwriting initiation delegator, in which case it is not the editor
         // view for which handwriting would be started. However, in almost all cases, the return
-        // values of View#isStylusHandwritingAvailable will be the same for the delegate view and
-        // the delegator editor view. So the delegate view can be used to decide whether handwriting
+        // values of View#isStylusHandwritingAvailable will be the same for the delegator view and
+        // the delegate editor view. So the delegator view can be used to decide whether handwriting
         // should be triggered.
         return view.isStylusHandwritingAvailable();
     }
@@ -677,7 +677,7 @@
     /** The helper method to check if the given view is still active for handwriting. */
     private static boolean isViewActive(@Nullable View view) {
         return view != null && view.isAttachedToWindow() && view.isAggregatedVisible()
-                && view.isAutoHandwritingEnabled();
+                && view.shouldInitiateHandwriting();
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5e19c67..8703609 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5488,7 +5488,6 @@
                 (TEXT_ALIGNMENT_DEFAULT << PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT) |
                 (PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT) |
                 (IMPORTANT_FOR_ACCESSIBILITY_DEFAULT << PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT);
-        mPrivateFlags4 = PFLAG4_AUTO_HANDWRITING_ENABLED;
 
         final ViewConfiguration configuration = ViewConfiguration.get(context);
         mTouchSlop = configuration.getScaledTouchSlop();
@@ -6213,7 +6212,7 @@
                     setPreferKeepClear(a.getBoolean(attr, false));
                     break;
                 case R.styleable.View_autoHandwritingEnabled:
-                    setAutoHandwritingEnabled(a.getBoolean(attr, true));
+                    setAutoHandwritingEnabled(a.getBoolean(attr, false));
                     break;
                 case R.styleable.View_handwritingBoundsOffsetLeft:
                     mHandwritingBoundsOffsetLeft = a.getDimension(attr, 0);
@@ -12150,7 +12149,7 @@
         if (getSystemGestureExclusionRects().isEmpty()
                 && collectPreferKeepClearRects().isEmpty()
                 && collectUnrestrictedPreferKeepClearRects().isEmpty()
-                && (info.mHandwritingArea == null || !isAutoHandwritingEnabled())) {
+                && (info.mHandwritingArea == null || !shouldInitiateHandwriting())) {
             if (info.mPositionUpdateListener != null) {
                 mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener);
                 info.mPositionUpdateListener = null;
@@ -12517,7 +12516,7 @@
 
     void updateHandwritingArea() {
         // If autoHandwritingArea is not enabled, do nothing.
-        if (!isAutoHandwritingEnabled()) return;
+        if (!shouldInitiateHandwriting()) return;
         final AttachInfo ai = mAttachInfo;
         if (ai != null) {
             ai.mViewRootImpl.getHandwritingInitiator().updateHandwritingAreasForView(this);
@@ -12525,6 +12524,16 @@
     }
 
     /**
+     * Returns true if a stylus {@link MotionEvent} within this view's bounds should initiate
+     * handwriting mode, either for this view ({@link #isAutoHandwritingEnabled()} is {@code true})
+     * or for a handwriting delegate view ({@link #getHandwritingDelegatorCallback()} is not {@code
+     * null}).
+     */
+    boolean shouldInitiateHandwriting() {
+        return isAutoHandwritingEnabled() || getHandwritingDelegatorCallback() != null;
+    }
+
+    /**
      * Sets a callback which should be called when a stylus {@link MotionEvent} occurs within this
      * view's bounds. The callback will be called from the UI thread.
      *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ddd3269..e0fda7e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4127,7 +4127,7 @@
     }
 
     private void fireAccessibilityFocusEventIfHasFocusedNode() {
-        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+        if (!mAccessibilityManager.isEnabled()) {
             return;
         }
         final View focusedView = mView.findFocus();
@@ -5179,8 +5179,12 @@
     }
 
     private boolean getAccessibilityFocusedRect(Rect bounds) {
-        final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext);
-        if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
+        if (mView == null) {
+            Slog.w(TAG, "calling getAccessibilityFocusedRect() while the mView is null");
+            return false;
+        }
+        if (!mAccessibilityManager.isEnabled()
+                || !mAccessibilityManager.isTouchExplorationEnabled()) {
             return false;
         }
 
@@ -7248,8 +7252,8 @@
                 && action != MotionEvent.ACTION_HOVER_EXIT) {
             return;
         }
-        AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
-        if (manager.isEnabled() && manager.isTouchExplorationEnabled()) {
+        if (mAccessibilityManager.isEnabled()
+                && mAccessibilityManager.isTouchExplorationEnabled()) {
             return;
         }
         if (mView == null) {
@@ -11049,7 +11053,7 @@
                 return;
             }
             // The accessibility may be turned off while we were waiting so check again.
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (mAccessibilityManager.isEnabled()) {
                 mLastEventTimeMillis = SystemClock.uptimeMillis();
                 AccessibilityEvent event = AccessibilityEvent.obtain();
                 event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e1de05b..79acfbb 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -801,6 +801,11 @@
                     mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId,
                             itemsAction.mServiceIntent));
                     isActionReplaced = true;
+                } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
+                        && intentAction.viewId == viewId) {
+                    mActions.set(i, new SetRemoteCollectionItemListAdapterAction(
+                            intentAction.viewId, intentAction.intent));
+                    isActionReplaced = true;
                 } else if (action instanceof ViewGroupActionAdd groupAction
                         && groupAction.mNestedViews != null) {
                     isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId);
@@ -822,6 +827,42 @@
         return isActionReplaced;
     }
 
+    /**
+     * @return True if has set remote adapter using service intent
+     * @hide
+     */
+    public boolean hasLegacyLists() {
+        if (mActions != null) {
+            for (int i = 0; i < mActions.size(); i++) {
+                Action action = mActions.get(i);
+                if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
+                        && itemsAction.mServiceIntent != null)
+                        || (action instanceof SetRemoteViewsAdapterIntent intentAction
+                                && intentAction.intent != null)
+                        || (action instanceof ViewGroupActionAdd groupAction
+                                && groupAction.mNestedViews != null
+                                && groupAction.mNestedViews.hasLegacyLists())) {
+                    return true;
+                }
+            }
+        }
+        if (mSizedRemoteViews != null) {
+            for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+                if (mSizedRemoteViews.get(i).hasLegacyLists()) {
+                    return true;
+                }
+            }
+        }
+        if (mLandscape != null && mLandscape.hasLegacyLists()) {
+            return true;
+        }
+        if (mPortrait != null && mPortrait.hasLegacyLists()) {
+            return true;
+        }
+
+        return false;
+    }
+
     private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
         if (icon != null && (icon.getType() == Icon.TYPE_URI
                 || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8899f5c..0001fe6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1856,6 +1856,7 @@
         boolean clickable = canInputOrMove || isClickable();
         boolean longClickable = canInputOrMove || isLongClickable();
         int focusable = getFocusable();
+        boolean isAutoHandwritingEnabled = true;
 
         n = a.getIndexCount();
         for (int i = 0; i < n; i++) {
@@ -1878,6 +1879,10 @@
                 case com.android.internal.R.styleable.View_longClickable:
                     longClickable = a.getBoolean(attr, longClickable);
                     break;
+
+                case com.android.internal.R.styleable.View_autoHandwritingEnabled:
+                    isAutoHandwritingEnabled = a.getBoolean(attr, true);
+                    break;
             }
         }
         a.recycle();
@@ -1891,6 +1896,7 @@
         }
         setClickable(clickable);
         setLongClickable(longClickable);
+        setAutoHandwritingEnabled(isAutoHandwritingEnabled);
 
         if (mEditor != null) mEditor.prepareCursorControllers();
 
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
new file mode 100644
index 0000000..560e41b
--- /dev/null
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.window.flags"
+
+# Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
+
+flag {
+    namespace: "windowing_sdk"
+    name: "sync_window_config_update_flag"
+    description: "Whether the feature to sync different window-related config updates is enabled"
+    bug: "260873529"
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f55501a..ffd640f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2267,6 +2267,14 @@
     <permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS"
         android:protectionLevel="signature" />
 
+    <!-- Allows system apps to call methods to register itself as a mDNS offload engine.
+        <p>Not for use by third-party or privileged applications.
+        @SystemApi
+        @hide This should only be used by system apps.
+    -->
+    <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
+        android:protectionLevel="signature" />
+
     <!-- ======================================= -->
     <!-- Permissions for short range, peripheral networks -->
     <!-- ======================================= -->
diff --git a/core/res/res/anim-ldrtl/activity_close_enter.xml b/core/res/res/anim-ldrtl/activity_close_enter.xml
index 6a699e7..0b48646 100644
--- a/core/res/res/anim-ldrtl/activity_close_enter.xml
+++ b/core/res/res/anim-ldrtl/activity_close_enter.xml
@@ -31,7 +31,7 @@
         android:duration="450" />
 
     <translate
-        android:fromXDelta="10%"
+        android:fromXDelta="96dp"
         android:toXDelta="0"
         android:fillEnabled="true"
         android:fillBefore="true"
@@ -41,11 +41,11 @@
         android:duration="450" />
 
     <extend
-        android:fromExtendLeft="10%"
+        android:fromExtendLeft="96dp"
         android:fromExtendTop="0"
         android:fromExtendRight="0"
         android:fromExtendBottom="0"
-        android:toExtendLeft="10%"
+        android:toExtendLeft="96dp"
         android:toExtendTop="0"
         android:toExtendRight="0"
         android:toExtendBottom="0"
diff --git a/core/res/res/anim-ldrtl/activity_close_exit.xml b/core/res/res/anim-ldrtl/activity_close_exit.xml
index 06a0d69..5277b9f 100644
--- a/core/res/res/anim-ldrtl/activity_close_exit.xml
+++ b/core/res/res/anim-ldrtl/activity_close_exit.xml
@@ -32,7 +32,7 @@
 
     <translate
         android:fromXDelta="0"
-        android:toXDelta="-10%"
+        android:toXDelta="-96dp"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
@@ -43,11 +43,11 @@
     <extend
         android:fromExtendLeft="0"
         android:fromExtendTop="0"
-        android:fromExtendRight="10%"
+        android:fromExtendRight="96dp"
         android:fromExtendBottom="0"
         android:toExtendLeft="0"
         android:toExtendTop="0"
-        android:toExtendRight="10%"
+        android:toExtendRight="96dp"
         android:toExtendBottom="0"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
         android:startOffset="0"
diff --git a/core/res/res/anim-ldrtl/activity_open_enter.xml b/core/res/res/anim-ldrtl/activity_open_enter.xml
index 7b18294..97d2cf9 100644
--- a/core/res/res/anim-ldrtl/activity_open_enter.xml
+++ b/core/res/res/anim-ldrtl/activity_open_enter.xml
@@ -30,7 +30,7 @@
         android:duration="83" />
 
     <translate
-        android:fromXDelta="-10%"
+        android:fromXDelta="-96dp"
         android:toXDelta="0"
         android:fillEnabled="true"
         android:fillBefore="true"
@@ -41,11 +41,11 @@
     <extend
         android:fromExtendLeft="0"
         android:fromExtendTop="0"
-        android:fromExtendRight="10%"
+        android:fromExtendRight="96dp"
         android:fromExtendBottom="0"
         android:toExtendLeft="0"
         android:toExtendTop="0"
-        android:toExtendRight="10%"
+        android:toExtendRight="96dp"
         android:toExtendBottom="0"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
         android:startOffset="0"
diff --git a/core/res/res/anim-ldrtl/activity_open_exit.xml b/core/res/res/anim-ldrtl/activity_open_exit.xml
index c29509e..2159029 100644
--- a/core/res/res/anim-ldrtl/activity_open_exit.xml
+++ b/core/res/res/anim-ldrtl/activity_open_exit.xml
@@ -31,7 +31,7 @@
 
     <translate
         android:fromXDelta="0"
-        android:toXDelta="10%"
+        android:toXDelta="96dp"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
@@ -40,11 +40,11 @@
         android:duration="450" />
 
     <extend
-        android:fromExtendLeft="10%"
+        android:fromExtendLeft="9dp"
         android:fromExtendTop="0"
         android:fromExtendRight="0"
         android:fromExtendBottom="0"
-        android:toExtendLeft="10%"
+        android:toExtendLeft="96dp"
         android:toExtendTop="0"
         android:toExtendRight="0"
         android:toExtendBottom="0"
diff --git a/core/res/res/anim-ldrtl/task_fragment_close_exit.xml b/core/res/res/anim-ldrtl/task_fragment_close_exit.xml
index c5a3654..58fcb1f 100644
--- a/core/res/res/anim-ldrtl/task_fragment_close_exit.xml
+++ b/core/res/res/anim-ldrtl/task_fragment_close_exit.xml
@@ -30,7 +30,7 @@
 
     <translate
         android:fromXDelta="0"
-        android:toXDelta="-10%"
+        android:toXDelta="-96dp"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index 0fefb51..22a1dd6 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -31,7 +31,7 @@
         android:duration="450" />
 
     <translate
-        android:fromXDelta="-10%"
+        android:fromXDelta="-96dp"
         android:toXDelta="0"
         android:fillEnabled="true"
         android:fillBefore="true"
@@ -43,11 +43,11 @@
     <extend
         android:fromExtendLeft="0"
         android:fromExtendTop="0"
-        android:fromExtendRight="10%"
+        android:fromExtendRight="96dp"
         android:fromExtendBottom="0"
         android:toExtendLeft="0"
         android:toExtendTop="0"
-        android:toExtendRight="10%"
+        android:toExtendRight="96dp"
         android:toExtendBottom="0"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
         android:startOffset="0"
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index f807c26..a671049 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -32,7 +32,7 @@
 
     <translate
         android:fromXDelta="0"
-        android:toXDelta="10%"
+        android:toXDelta="96dp"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
@@ -41,11 +41,11 @@
         android:duration="450" />
 
     <extend
-        android:fromExtendLeft="10%"
+        android:fromExtendLeft="96dp"
         android:fromExtendTop="0"
         android:fromExtendRight="0"
         android:fromExtendBottom="0"
-        android:toExtendLeft="10%"
+        android:toExtendLeft="96dp"
         android:toExtendTop="0"
         android:toExtendRight="0"
         android:toExtendBottom="0"
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index 1674dab..f3172e4 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -30,7 +30,7 @@
         android:duration="83" />
 
     <translate
-        android:fromXDelta="10%"
+        android:fromXDelta="96dp"
         android:toXDelta="0"
         android:fillEnabled="true"
         android:fillBefore="true"
@@ -39,11 +39,11 @@
         android:duration="450" />
 
     <extend
-        android:fromExtendLeft="10%"
+        android:fromExtendLeft="96dp"
         android:fromExtendTop="0"
         android:fromExtendRight="0"
         android:fromExtendBottom="0"
-        android:toExtendLeft="10%"
+        android:toExtendLeft="96dp"
         android:toExtendTop="0"
         android:toExtendRight="0"
         android:toExtendBottom="0"
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 372f2c8..d84827b 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -31,7 +31,7 @@
 
     <translate
         android:fromXDelta="0"
-        android:toXDelta="-10%"
+        android:toXDelta="-96dp"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
@@ -42,11 +42,11 @@
     <extend
         android:fromExtendLeft="0"
         android:fromExtendTop="0"
-        android:fromExtendRight="10%"
+        android:fromExtendRight="96dp"
         android:fromExtendBottom="0"
         android:toExtendLeft="0"
         android:toExtendTop="0"
-        android:toExtendRight="10%"
+        android:toExtendRight="96dp"
         android:toExtendBottom="0"
         android:interpolator="@interpolator/fast_out_extra_slow_in"
         android:startOffset="0"
diff --git a/core/res/res/anim/task_fragment_close_exit.xml b/core/res/res/anim/task_fragment_close_exit.xml
index 84d8b7e..6454085 100644
--- a/core/res/res/anim/task_fragment_close_exit.xml
+++ b/core/res/res/anim/task_fragment_close_exit.xml
@@ -30,7 +30,7 @@
 
     <translate
         android:fromXDelta="0"
-        android:toXDelta="10%"
+        android:toXDelta="96dp"
         android:fillEnabled="true"
         android:fillBefore="true"
         android:fillAfter="true"
diff --git a/core/res/res/anim/task_fragment_open_enter.xml b/core/res/res/anim/task_fragment_open_enter.xml
index 87ee179..5f57ed5 100644
--- a/core/res/res/anim/task_fragment_open_enter.xml
+++ b/core/res/res/anim/task_fragment_open_enter.xml
@@ -27,7 +27,7 @@
         android:startOffset="50"
         android:duration="83" />
     <translate
-        android:fromXDelta="10%"
+        android:fromXDelta="96dp"
         android:toXDelta="0"
         android:fillEnabled="true"
         android:fillBefore="true"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index aeeaaca..281053b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1210,13 +1210,16 @@
     <integer name="config_triplePressOnStemPrimaryBehavior">0</integer>
 
     <!-- Control the behavior when the user short presses the stem primary button.
-         Stem primary button is only used on watch form factor. If a device is not
-         a watch, setting this config is no-op.
-            0 - Nothing
-            1 - Go to launch all apps
+        Stem primary button is only used on watch form factor. If a device is not
+        a watch, setting this config is no-op.
+           0 - Nothing
+           1 - Go to launch all apps
+           2 - Launch target activity defined by config_primaryShortPressTargetActivity if available
     -->
     <integer name="config_shortPressOnStemPrimaryBehavior">0</integer>
 
+   <!-- Activity name for the default target activity to be launched. [DO NOT TRANSLATE] -->
+   <string name="config_primaryShortPressTargetActivity" translatable="false"></string>
 
     <!-- Control the behavior of the search key.
             0 - Launch default search activity
@@ -5356,6 +5359,7 @@
         <item>1,1,1.0,0,1</item>
         <item>1,1,1.0,.4,1</item>
         <item>1,1,1.0,.15,15</item>
+        <item>0,0,0.7,0,1</item>
     </string-array>
 
     <!-- The integer index of the selected option in config_udfps_touch_detection_options -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index bda194a..6bb87f3 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -73,7 +73,7 @@
          CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
          If 0, the device always switch to the higher score SIM.
          If < 0, the network type and signal strength based auto switch is disabled. -->
-    <integer name="auto_data_switch_score_tolerance">3000</integer>
+    <integer name="auto_data_switch_score_tolerance">-1</integer>
     <java-symbol type="integer" name="auto_data_switch_score_tolerance" />
 
     <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8330f7b..aa0cbfa 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -466,6 +466,7 @@
   <java-symbol type="integer" name="config_shortPressOnSleepBehavior" />
   <java-symbol type="integer" name="config_longPressOnStemPrimaryBehavior" />
   <java-symbol type="integer" name="config_shortPressOnStemPrimaryBehavior" />
+  <java-symbol type="string" name="config_primaryShortPressTargetActivity" />
   <java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
   <java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
   <java-symbol type="string" name="config_doublePressOnPowerTargetActivity" />
diff --git a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
index 9acb99a..b794352 100644
--- a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
+++ b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
@@ -445,7 +445,9 @@
                     Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION)
         }
         handlerThread = HandlerThread("PackageSessionTests")
-        handlerThread?.start()
-        handler = Handler(handlerThread?.looper)
+        handlerThread?.let {
+            it.start()
+            handler = Handler(it.looper)
+        }
     }
 }
diff --git a/core/tests/coretests/res/values-id/strings.xml b/core/tests/coretests/res/values-id/strings.xml
new file mode 100644
index 0000000..6d71c90
--- /dev/null
+++ b/core/tests/coretests/res/values-id/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Used in ResourcesLocaleTest. -->
+    <string name="locale_test_res_1">Pengujian ID</string>
+</resources>
diff --git a/core/tests/coretests/res/values-in/strings.xml b/core/tests/coretests/res/values-in/strings.xml
new file mode 100644
index 0000000..6384660
--- /dev/null
+++ b/core/tests/coretests/res/values-in/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Used in ResourcesLocaleTest. -->
+    <string name="locale_test_res_2">Pengujian IN</string>
+</resources>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index e51eab6..09e1c69 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -131,6 +131,13 @@
 
     <string name="textview_hebrew_text">&#x05DD;&#x05DE;ab?!</string>
 
+    <!-- Used in ResourcesLocaleTest. Also defined in values-id. "id" is the new ISO code for Indonesian. -->
+    <string name="locale_test_res_1">Testing ID</string>
+    <!-- Used in ResourcesLocaleTest. Also defined in values-in. "in" is the deprecated ISO code for Indonesian. -->
+    <string name="locale_test_res_2">Testing IN</string>
+    <!-- Used in ResourcesLocaleTest. -->
+    <string name="locale_test_res_3">Testing EN</string>
+
     <!-- SizeAdaptiveLayout -->
     <string name="first">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.</string>
     <string name="actor">Abe Lincoln</string>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index 25c3db5..26e4349 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.content.Context;
 import android.os.FileUtils;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
@@ -97,4 +98,24 @@
         assertEquals(Locale.forLanguageTag("pl-PL"),
                 resources.getConfiguration().getLocales().get(0));
     }
+
+    @SmallTest
+    public void testDeprecatedISOLanguageCode() {
+        assertResGetString(Locale.US, R.string.locale_test_res_1, "Testing ID");
+        assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_2, "Pengujian IN");
+        assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_3, "Testing EN");
+        assertResGetString(new Locale("id"), R.string.locale_test_res_2, "Pengujian IN");
+        assertResGetString(new Locale("id"), R.string.locale_test_res_3, "Testing EN");
+        // The new ISO code "id" isn't supported yet, and thus the values-id are ignored.
+        assertResGetString(new Locale("id"), R.string.locale_test_res_1, "Testing ID");
+        assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_1, "Testing ID");
+    }
+
+    private void assertResGetString(Locale locale, int resId, String expectedString) {
+        LocaleList locales = new LocaleList(locale);
+        final Configuration config = new Configuration();
+        config.setLocales(locales);
+        Context newContext = getContext().createConfigurationContext(config);
+        assertEquals(expectedString, newContext.getResources().getString(resId));
+    }
 }
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 394ff0a..a0d8183 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -505,45 +505,32 @@
 
     @Test
     public void testRoundStorageSize() throws Exception {
-        final long GB1 = DataUnit.GIGABYTES.toBytes(1);
-        final long GiB1 = DataUnit.GIBIBYTES.toBytes(1);
-        final long GB2 = DataUnit.GIGABYTES.toBytes(2);
-        final long GiB2 = DataUnit.GIBIBYTES.toBytes(2);
-        final long GiB128 = DataUnit.GIBIBYTES.toBytes(128);
-        final long GB256 = DataUnit.GIGABYTES.toBytes(256);
-        final long GiB256 = DataUnit.GIBIBYTES.toBytes(256);
-        final long GB512 = DataUnit.GIGABYTES.toBytes(512);
-        final long GiB512 = DataUnit.GIBIBYTES.toBytes(512);
-        final long TB1 = DataUnit.TERABYTES.toBytes(1);
-        final long TiB1 = DataUnit.TEBIBYTES.toBytes(1);
-        final long TB2 = DataUnit.TERABYTES.toBytes(2);
-        final long TiB2 = DataUnit.TEBIBYTES.toBytes(2);
-        final long TB4 = DataUnit.TERABYTES.toBytes(4);
-        final long TiB4 = DataUnit.TEBIBYTES.toBytes(4);
-        final long TB8 = DataUnit.TERABYTES.toBytes(8);
-        final long TiB8 = DataUnit.TEBIBYTES.toBytes(8);
+        final long M256 = DataUnit.MEGABYTES.toBytes(256);
+        final long M512 = DataUnit.MEGABYTES.toBytes(512);
+        final long G1 = DataUnit.GIGABYTES.toBytes(1);
+        final long G2 = DataUnit.GIGABYTES.toBytes(2);
+        final long G32 = DataUnit.GIGABYTES.toBytes(32);
+        final long G64 = DataUnit.GIGABYTES.toBytes(64);
+        final long G512 = DataUnit.GIGABYTES.toBytes(512);
+        final long G1000 = DataUnit.TERABYTES.toBytes(1);
+        final long G2000 = DataUnit.TERABYTES.toBytes(2);
 
-        assertEquals(GB1, roundStorageSize(GB1 - 1));
-        assertEquals(GB1, roundStorageSize(GB1));
-        assertEquals(GB1, roundStorageSize(GB1 + 1));
-        assertEquals(GB1, roundStorageSize(GiB1 - 1));
-        assertEquals(GB1, roundStorageSize(GiB1));
-        assertEquals(GB2, roundStorageSize(GiB1 + 1));
-        assertEquals(GB2, roundStorageSize(GiB2));
+        assertEquals(M256, roundStorageSize(M256 - 1));
+        assertEquals(M256, roundStorageSize(M256));
+        assertEquals(M512, roundStorageSize(M256 + 1));
+        assertEquals(M512, roundStorageSize(M512 - 1));
+        assertEquals(M512, roundStorageSize(M512));
+        assertEquals(G1, roundStorageSize(M512 + 1));
+        assertEquals(G1, roundStorageSize(G1));
+        assertEquals(G2, roundStorageSize(G1 + 1));
 
-        assertEquals(GB256, roundStorageSize(GiB128 + 1));
-        assertEquals(GB256, roundStorageSize(GiB256));
-        assertEquals(GB512, roundStorageSize(GiB256 + 1));
-        assertEquals(GB512, roundStorageSize(GiB512));
-        assertEquals(TB1, roundStorageSize(GiB512 + 1));
-        assertEquals(TB1, roundStorageSize(TiB1));
-        assertEquals(TB2, roundStorageSize(TiB1 + 1));
-        assertEquals(TB2, roundStorageSize(TiB2));
-        assertEquals(TB4, roundStorageSize(TiB2 + 1));
-        assertEquals(TB4, roundStorageSize(TiB4));
-        assertEquals(TB8, roundStorageSize(TiB4 + 1));
-        assertEquals(TB8, roundStorageSize(TiB8));
-        assertEquals(TB1, roundStorageSize(1013077688320L)); // b/268571529
+        assertEquals(G32, roundStorageSize(G32 - 1));
+        assertEquals(G32, roundStorageSize(G32));
+        assertEquals(G64, roundStorageSize(G32 + 1));
+
+        assertEquals(G512, roundStorageSize(G512 - 1));
+        assertEquals(G1000, roundStorageSize(G512 + 1));
+        assertEquals(G2000, roundStorageSize(G1000 + 1));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index f1eef75..c46118d 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -245,7 +245,7 @@
 
     @Test
     public void onTouchEvent_tryAcceptDelegation_delegatorCallbackCreatesInputConnection() {
-        View delegateView = new View(mContext);
+        View delegateView = new EditText(mContext);
         delegateView.setIsHandwritingDelegate(true);
 
         mTestView1.setHandwritingDelegatorCallback(
@@ -266,7 +266,7 @@
 
     @Test
     public void onTouchEvent_tryAcceptDelegation_delegatorCallbackFocusesDelegate() {
-        View delegateView = new View(mContext);
+        View delegateView = new EditText(mContext);
         delegateView.setIsHandwritingDelegate(true);
         mHandwritingInitiator.onInputConnectionCreated(delegateView);
         reset(mHandwritingInitiator);
diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
new file mode 100644
index 0000000..a8b4032
--- /dev/null
+++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 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.window.flags;
+
+import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link com.android.window.flags.Flags}
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:WindowFlagsTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowFlagsTest {
+
+    @Test
+    public void testSyncWindowConfigUpdateFlag() {
+        // No crash when accessing the flag.
+        syncWindowConfigUpdateFlag();
+    }
+}
diff --git a/core/tests/utiltests/src/android/util/IntArrayTest.java b/core/tests/utiltests/src/android/util/IntArrayTest.java
index caa7312..ad5c4ee 100644
--- a/core/tests/utiltests/src/android/util/IntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/IntArrayTest.java
@@ -55,9 +55,11 @@
         a.add(5, 20);
         assertThat(a.get(5)).isEqualTo(20);
         assertThat(a.indexOf(20)).isEqualTo(5);
+        assertThat(a.contains(20)).isTrue();
         verify(a, 1, 2, 0, 0, 0, 20, 10, 0, 0);
 
         assertThat(a.indexOf(99)).isEqualTo(-1);
+        assertThat(a.contains(99)).isFalse();
 
         a.resize(15);
         a.set(14, 30);
@@ -71,6 +73,7 @@
         backingArray[2] = 30;
         verify(a, backingArray);
         assertThat(a.indexOf(30)).isEqualTo(2);
+        assertThat(a.contains(30)).isTrue();
 
         a.resize(2);
         assertThat(backingArray[2]).isEqualTo(0);
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index e27cd97..31092536 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -439,8 +439,10 @@
 key usage 0x0c0173 MEDIA_AUDIO_TRACK
 key usage 0x0c019C PROFILE_SWITCH
 key usage 0x0c01A2 ALL_APPS
-key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
-key usage 0x0d005a STYLUS_BUTTON_SECONDARY
+# TODO(b/297094448): Add stylus button mappings as a fallback when we have a way to determine
+#   if a device can actually report it.
+# key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
+# key usage 0x0d005a STYLUS_BUTTON_SECONDARY
 
 # Joystick and game controller axes.
 # Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
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 81384ca..a5ee19e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -261,10 +261,6 @@
             // Updates the Split
             final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
             final WindowContainerTransaction wct = transactionRecord.getTransaction();
-
-            mPresenter.setTaskFragmentIsolatedNavigation(wct,
-                    splitPinContainer.getSecondaryContainer().getTaskFragmentToken(),
-                    true /* isolatedNav */);
             mPresenter.updateSplitContainer(splitPinContainer, wct);
             transactionRecord.apply(false /* shouldApplyIndependently */);
             updateCallbackIfNecessary();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 5de6acf..896fe61 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -382,6 +382,19 @@
         }
         setCompanionTaskFragment(wct, primaryContainer.getTaskFragmentToken(),
                 secondaryContainer.getTaskFragmentToken(), splitRule, isStacked);
+
+        // Setting isolated navigation and clear non-sticky pinned container if needed.
+        final SplitPinRule splitPinRule =
+                splitRule instanceof SplitPinRule ? (SplitPinRule) splitRule : null;
+        if (splitPinRule == null) {
+            return;
+        }
+
+        setTaskFragmentIsolatedNavigation(wct, secondaryContainer.getTaskFragmentToken(),
+                !isStacked /* isolatedNav */);
+        if (isStacked && !splitPinRule.isSticky()) {
+            secondaryContainer.getTaskContainer().removeSplitPinContainer();
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 4640106..9b80063 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -245,8 +245,8 @@
 
     private boolean shouldShowBackdrop(@NonNull TransitionInfo info,
             @NonNull TransitionInfo.Change change) {
-        final Animation a = loadAttributeAnimation(info, change, WALLPAPER_TRANSITION_NONE,
-                mTransitionAnimation, false);
+        final Animation a = loadAttributeAnimation(info.getType(), info, change,
+                WALLPAPER_TRANSITION_NONE, mTransitionAnimation, false);
         return a != null && a.getShowBackdrop();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index ff67110..9a2b812 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -851,7 +851,10 @@
         return mAppIntent;
     }
 
-    boolean isAppBubble() {
+    /**
+     * Returns whether this bubble is from an app versus a notification.
+     */
+    public boolean isAppBubble() {
         return mIsAppBubble;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
index 250e010..76662c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
@@ -52,6 +52,11 @@
     private static final boolean FORCE_SHOW_USER_EDUCATION = false;
     private static final String FORCE_SHOW_USER_EDUCATION_SETTING =
             "force_show_bubbles_user_education";
+    /**
+     * When set to true, bubbles user education flow never shows up.
+     */
+    private static final String FORCE_HIDE_USER_EDUCATION_SETTING =
+            "force_hide_bubbles_user_education";
 
     /**
      * @return whether we should force show user education for bubbles. Used for debugging & demos.
@@ -62,6 +67,14 @@
         return FORCE_SHOW_USER_EDUCATION || forceShow;
     }
 
+    /**
+     * @return whether we should never show user education for bubbles. Used in tests.
+     */
+    static boolean neverShowUserEducation(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                FORCE_HIDE_USER_EDUCATION_SETTING, 0) != 0;
+    }
+
     static String formatBubblesString(List<Bubble> bubbles, BubbleViewProvider selected) {
         StringBuilder sb = new StringBuilder();
         for (Bubble bubble : bubbles) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 2c10065..ea7053d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -653,14 +653,38 @@
     }
 
     /**
-     * @return the stack position to use if we don't have a saved location or if user education
-     * is being shown.
+     * Returns whether the {@link #getRestingPosition()} is equal to the default start position
+     * initialized for bubbles, if {@code true} this means the user hasn't moved the bubble
+     * from the initial start position (or they haven't received a bubble yet).
+     */
+    public boolean hasUserModifiedDefaultPosition() {
+        PointF defaultStart = getDefaultStartPosition();
+        return mRestingStackPosition != null
+                && !mRestingStackPosition.equals(defaultStart);
+    }
+
+    /**
+     * Returns the stack position to use if we don't have a saved location or if user education
+     * is being shown, for a normal bubble.
      */
     public PointF getDefaultStartPosition() {
-        // Start on the left if we're in LTR, right otherwise.
-        final boolean startOnLeft =
-                mContext.getResources().getConfiguration().getLayoutDirection()
-                        != LAYOUT_DIRECTION_RTL;
+        return getDefaultStartPosition(false /* isAppBubble */);
+    }
+
+    /**
+     * The stack position to use if we don't have a saved location or if user education
+     * is being shown.
+     *
+     * @param isAppBubble whether this start position is for an app bubble or not.
+     */
+    public PointF getDefaultStartPosition(boolean isAppBubble) {
+        final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
+        // Normal bubbles start on the left if we're in LTR, right otherwise.
+        // TODO (b/294284894): update language around "app bubble" here
+        // App bubbles start on the right in RTL, left otherwise.
+        final boolean startOnLeft = isAppBubble
+                ? layoutDirection == LAYOUT_DIRECTION_RTL
+                : layoutDirection != LAYOUT_DIRECTION_RTL;
         final RectF allowableStackPositionRegion = getAllowableStackPositionRegion(
                 1 /* default starts with 1 bubble */);
         if (isLargeScreen()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 8ae12c7..52c9bf8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1284,6 +1284,12 @@
         if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
             Log.d(TAG, "Show manage edu: " + shouldShow);
         }
+        if (shouldShow && BubbleDebugConfig.neverShowUserEducation(mContext)) {
+            if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+                Log.d(TAG, "Want to show manage edu, but it is forced hidden");
+            }
+            return false;
+        }
         return shouldShow;
     }
 
@@ -1316,6 +1322,12 @@
         if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
             Log.d(TAG, "Show stack edu: " + shouldShow);
         }
+        if (shouldShow && BubbleDebugConfig.neverShowUserEducation(mContext)) {
+            if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
+                Log.d(TAG, "Want to show stack edu, but it is forced hidden");
+            }
+            return false;
+        }
         return shouldShow;
     }
 
@@ -1763,13 +1775,26 @@
             return;
         }
 
+        if (firstBubble && bubble.isAppBubble() && !mPositioner.hasUserModifiedDefaultPosition()) {
+            // TODO (b/294284894): update language around "app bubble" here
+            // If it's an app bubble and we don't have a previous resting position, update the
+            // controllers to use the default position for the app bubble (it'd be different from
+            // the position initialized with the controllers originally).
+            PointF startPosition =  mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+            mStackOnLeftOrWillBe = mPositioner.isStackOnLeft(startPosition);
+            mStackAnimationController.setStackPosition(startPosition);
+            mExpandedAnimationController.setCollapsePoint(startPosition);
+            // Set the translation x so that this bubble will animate in from the same side they
+            // expand / collapse on.
+            bubble.getIconView().setTranslationX(startPosition.x);
+        } else if (firstBubble) {
+            mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
+        }
+
         mBubbleContainer.addView(bubble.getIconView(), 0,
                 new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
                         mPositioner.getBubbleSize()));
 
-        if (firstBubble) {
-            mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
-        }
         // Set the dot position to the opposite of the side the stack is resting on, since the stack
         // resting slightly off-screen would result in the dot also being off-screen.
         bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index c20733a..4d7042b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -131,6 +131,16 @@
 
     private BubbleStackView mBubbleStackView;
 
+    /**
+     * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
+     * the rest of the bubbles to animate to fill the gap.
+     */
+    private boolean mBubbleDraggedOutEnough = false;
+
+    /** End action to run when the lead bubble's expansion animation completes. */
+    @Nullable
+    private Runnable mLeadBubbleEndAction;
+
     public ExpandedAnimationController(BubblePositioner positioner,
             Runnable onBubbleAnimatedOutAction, BubbleStackView stackView) {
         mPositioner = positioner;
@@ -141,14 +151,12 @@
     }
 
     /**
-     * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
-     * the rest of the bubbles to animate to fill the gap.
+     * Overrides the collapse location without actually collapsing the stack.
+     * @param point the new collapse location.
      */
-    private boolean mBubbleDraggedOutEnough = false;
-
-    /** End action to run when the lead bubble's expansion animation completes. */
-    @Nullable
-    private Runnable mLeadBubbleEndAction;
+    public void setCollapsePoint(PointF point) {
+        mCollapsePoint = point;
+    }
 
     /**
      * Animates expanding the bubbles into a row along the top of the screen, optionally running an
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 4bb1ab4..aad2683 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -297,9 +297,6 @@
 
     /** Whether the stack is on the left side of the screen. */
     public boolean isStackOnLeftSide() {
-        if (mLayout == null || !isStackPositionSet()) {
-            return true; // Default to left, which is where it starts by default.
-        }
         return mPositioner.isStackOnLeft(mStackPosition);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
index 8b5283d..1fd22d0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
@@ -85,7 +85,7 @@
         canvas.drawPath(path, paint)
     }
 
-    override fun onBoundsChange(bounds: Rect?) {
+    override fun onBoundsChange(bounds: Rect) {
         requestPathUpdate()
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
index a141ff9..4abb35c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.pip.PipUtils
 
 class PipAppOpsListener(
     private val mContext: Context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
index 2719cd2..427a555 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipMediaController.kt
@@ -33,7 +33,6 @@
 import android.os.HandlerExecutor
 import android.os.UserHandle
 import com.android.wm.shell.R
-import com.android.wm.shell.pip.PipUtils
 import java.util.function.Consumer
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
new file mode 100644
index 0000000..84feb03
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common.pip
+
+import android.app.ActivityTaskManager
+import android.app.RemoteAction
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import android.content.Context
+import android.os.RemoteException
+import android.os.SystemProperties
+import android.util.DisplayMetrics
+import android.util.Log
+import android.util.Pair
+import android.util.TypedValue
+import android.window.TaskSnapshot
+import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import kotlin.math.abs
+
+/** A class that includes convenience methods.  */
+object PipUtils {
+    private const val TAG = "PipUtils"
+
+    // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
+    private const val EPSILON = 1e-7
+    private const val ENABLE_PIP2_IMPLEMENTATION = "persist.wm.debug.enable_pip2_implementation"
+
+    /**
+     * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
+     * The component name may be null if no such activity exists.
+     */
+    @JvmStatic
+    fun getTopPipActivity(context: Context): Pair<ComponentName?, Int> {
+        try {
+            val sysUiPackageName = context.packageName
+            val pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
+                WindowConfiguration.WINDOWING_MODE_PINNED,
+                WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+            )
+            if (pinnedTaskInfo?.childTaskIds != null && pinnedTaskInfo.childTaskIds.isNotEmpty()) {
+                for (i in pinnedTaskInfo.childTaskNames.indices.reversed()) {
+                    val cn = ComponentName.unflattenFromString(
+                        pinnedTaskInfo.childTaskNames[i]
+                    )
+                    if (cn != null && cn.packageName != sysUiPackageName) {
+                        return Pair(cn, pinnedTaskInfo.childTaskUserIds[i])
+                    }
+                }
+            }
+        } catch (e: RemoteException) {
+            ProtoLog.w(
+                ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: Unable to get pinned stack.", TAG
+            )
+        }
+        return Pair(null, 0)
+    }
+
+    /**
+     * @return the pixels for a given dp value.
+     */
+    @JvmStatic
+    fun dpToPx(dpValue: Float, dm: DisplayMetrics?): Int {
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, dm).toInt()
+    }
+
+    /**
+     * @return true if the aspect ratios differ
+     */
+    @JvmStatic
+    fun aspectRatioChanged(aspectRatio1: Float, aspectRatio2: Float): Boolean {
+        return abs(aspectRatio1 - aspectRatio2) > EPSILON
+    }
+
+    /**
+     * Checks whether title, description and intent match.
+     * Comparing icons would be good, but using equals causes false negatives
+     */
+    @JvmStatic
+    fun remoteActionsMatch(action1: RemoteAction?, action2: RemoteAction?): Boolean {
+        if (action1 === action2) return true
+        if (action1 == null || action2 == null) return false
+        return action1.isEnabled == action2.isEnabled &&
+                action1.shouldShowIcon() == action2.shouldShowIcon() &&
+                action1.title == action2.title &&
+                action1.contentDescription == action2.contentDescription &&
+                action1.actionIntent == action2.actionIntent
+    }
+
+    /**
+     * Returns true if the actions in the lists match each other according to
+     * [ ][PipUtils.remoteActionsMatch], including their position.
+     */
+    @JvmStatic
+    fun remoteActionsChanged(list1: List<RemoteAction?>?, list2: List<RemoteAction?>?): Boolean {
+        if (list1 == null && list2 == null) {
+            return false
+        }
+        if (list1 == null || list2 == null) {
+            return true
+        }
+        if (list1.size != list2.size) {
+            return true
+        }
+        for (i in list1.indices) {
+            if (!remoteActionsMatch(list1[i], list2[i])) {
+                return true
+            }
+        }
+        return false
+    }
+
+    /** @return [TaskSnapshot] for a given task id.
+     */
+    @JvmStatic
+    fun getTaskSnapshot(taskId: Int, isLowResolution: Boolean): TaskSnapshot? {
+        return if (taskId <= 0) null else try {
+            ActivityTaskManager.getService().getTaskSnapshot(
+                taskId, isLowResolution, false /* takeSnapshotIfNeeded */
+            )
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Failed to get task snapshot, taskId=$taskId", e)
+            null
+        }
+    }
+
+    @JvmStatic
+    val isPip2ExperimentEnabled: Boolean
+        get() = SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false)
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 7bf0893..c111ce6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -34,6 +34,7 @@
 import android.view.Display;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -59,6 +60,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 /**
@@ -80,6 +82,9 @@
 
     private static final String TAG = "CompatUIController";
 
+    // The time to wait before education and button hiding
+    private static final int DISAPPEAR_DELAY_MS = 5000;
+
     /** Whether the IME is shown on display id. */
     private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
 
@@ -158,6 +163,9 @@
     @NonNull
     private final CompatUIShellCommandHandler mCompatUIShellCommandHandler;
 
+    @NonNull
+    private final Function<Integer, Integer> mDisappearTimeSupplier;
+
     @Nullable
     private CompatUICallback mCompatUICallback;
 
@@ -176,7 +184,8 @@
             @NonNull Lazy<Transitions> transitionsLazy,
             @NonNull DockStateReader dockStateReader,
             @NonNull CompatUIConfiguration compatUIConfiguration,
-            @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler) {
+            @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler,
+            @NonNull AccessibilityManager accessibilityManager) {
         mContext = context;
         mShellController = shellController;
         mDisplayController = displayController;
@@ -189,6 +198,8 @@
         mDockStateReader = dockStateReader;
         mCompatUIConfiguration = compatUIConfiguration;
         mCompatUIShellCommandHandler = compatUIShellCommandHandler;
+        mDisappearTimeSupplier = flags -> accessibilityManager.getRecommendedTimeoutMillis(
+                DISAPPEAR_DELAY_MS, flags);
         shellInit.addInitCallback(this::onInit, this);
     }
 
@@ -510,7 +521,8 @@
             ShellTaskOrganizer.TaskListener taskListener) {
         return new ReachabilityEduWindowManager(context, taskInfo, mSyncQueue,
                 taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
-                mCompatUIConfiguration, mMainExecutor, this::onInitialReachabilityEduDismissed);
+                mCompatUIConfiguration, mMainExecutor, this::onInitialReachabilityEduDismissed,
+                mDisappearTimeSupplier);
     }
 
     private void onInitialReachabilityEduDismissed(@NonNull TaskInfo taskInfo,
@@ -556,7 +568,8 @@
             @Nullable ShellTaskOrganizer.TaskListener taskListener) {
         return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue,
                 taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
-                mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor);
+                mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor,
+                mDisappearTimeSupplier);
     }
 
     private void launchUserAspectRatioSettings(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
index 9de3f9d..5612bc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
@@ -28,6 +28,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
@@ -37,15 +38,13 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 
 import java.util.function.BiConsumer;
+import java.util.function.Function;
 
 /**
  * Window manager for the reachability education
  */
 class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
 
-    // The time to wait before hiding the education
-    private static final long DISAPPEAR_DELAY_MS = 4000L;
-
     private static final int REACHABILITY_LEFT_OR_UP_POSITION = 0;
     private static final int REACHABILITY_RIGHT_OR_BOTTOM_POSITION = 2;
 
@@ -77,6 +76,8 @@
 
     private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnDismissCallback;
 
+    private final Function<Integer, Integer> mDisappearTimeSupplier;
+
     @Nullable
     @VisibleForTesting
     ReachabilityEduLayout mLayout;
@@ -85,7 +86,8 @@
             SyncTransactionQueue syncQueue,
             ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
             CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor,
-            BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onDismissCallback) {
+            BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onDismissCallback,
+            Function<Integer, Integer> disappearTimeSupplier) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled;
         mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition;
@@ -95,6 +97,7 @@
         mCompatUIConfiguration = compatUIConfiguration;
         mMainExecutor = mainExecutor;
         mOnDismissCallback = onDismissCallback;
+        mDisappearTimeSupplier = disappearTimeSupplier;
     }
 
     @Override
@@ -215,7 +218,12 @@
     }
 
     void updateHideTime() {
-        mNextHideTime = SystemClock.uptimeMillis() + DISAPPEAR_DELAY_MS;
+        mNextHideTime = SystemClock.uptimeMillis() + getDisappearTimeMs();
+    }
+
+    private long getDisappearTimeMs() {
+        return mDisappearTimeSupplier.apply(
+                AccessibilityManager.FLAG_CONTENT_ICONS | AccessibilityManager.FLAG_CONTENT_TEXT);
     }
 
     private void updateVisibilityOfViews() {
@@ -248,14 +256,15 @@
                     availableHeight, mCompatUIConfiguration, lastTaskInfo);
             if (!mHasLetterboxSizeChanged) {
                 updateHideTime();
-                mMainExecutor.executeDelayed(this::hideReachability, DISAPPEAR_DELAY_MS);
+                final long disappearTimeMs = getDisappearTimeMs();
+                mMainExecutor.executeDelayed(this::hideReachability, disappearTimeMs);
                 // If reachability education has been seen for the first time, trigger callback to
                 // display aspect ratio settings button once reachability education disappears
                 if (hasShownHorizontalReachabilityEduFirstTime(hasSeenHorizontalReachabilityEdu)
                         || hasShownVerticalReachabilityEduFirstTime(
                         hasSeenVerticalReachabilityEdu)) {
                     mMainExecutor.executeDelayed(this::triggerOnDismissCallback,
-                            DISAPPEAR_DELAY_MS);
+                            disappearTimeMs);
                 }
             }
             mHasUserDoubleTapped = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index bd53dc7..cbff464 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -26,6 +26,7 @@
 import android.os.SystemClock;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
@@ -36,6 +37,7 @@
 import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
 
 import java.util.function.BiConsumer;
+import java.util.function.Function;
 
 /**
  * Window manager for the user aspect ratio settings button which allows users to go to
@@ -45,12 +47,12 @@
 
     private static final long SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 500L;
 
-    private static final long HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 4000L;
-
     private long mNextButtonHideTimeMs = -1L;
 
     private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnButtonClicked;
 
+    private final Function<Integer, Integer> mDisappearTimeSupplier;
+
     private final ShellExecutor mShellExecutor;
 
     @VisibleForTesting
@@ -69,12 +71,14 @@
             @Nullable ShellTaskOrganizer.TaskListener taskListener,
             @NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState,
             @NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked,
-            @NonNull ShellExecutor shellExecutor) {
+            @NonNull ShellExecutor shellExecutor,
+            @NonNull Function<Integer, Integer> disappearTimeSupplier) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mShellExecutor = shellExecutor;
         mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
         mCompatUIHintsState = compatUIHintsState;
         mOnButtonClicked = onButtonClicked;
+        mDisappearTimeSupplier = disappearTimeSupplier;
     }
 
     @Override
@@ -140,9 +144,9 @@
             return;
         }
         mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true);
-        mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
-        mShellExecutor.executeDelayed(this::hideUserAspectRatioButton,
-                HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
+        final long disappearTimeMs = getDisappearTimeMs();
+        mNextButtonHideTimeMs = updateHideTime(disappearTimeMs);
+        mShellExecutor.executeDelayed(this::hideUserAspectRatioButton, disappearTimeMs);
     }
 
     @Override
@@ -167,9 +171,9 @@
         if (mHasUserAspectRatioSettingsButton) {
             mShellExecutor.executeDelayed(this::showUserAspectRatioButton,
                     SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
-            mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
-            mShellExecutor.executeDelayed(this::hideUserAspectRatioButton,
-                    HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
+            final long disappearTimeMs = getDisappearTimeMs();
+            mNextButtonHideTimeMs = updateHideTime(disappearTimeMs);
+            mShellExecutor.executeDelayed(this::hideUserAspectRatioButton, disappearTimeMs);
         } else {
             mShellExecutor.removeCallbacks(this::showUserAspectRatioButton);
             mShellExecutor.execute(this::hideUserAspectRatioButton);
@@ -208,4 +212,8 @@
                 && (taskInfo.topActivityBoundsLetterboxed
                     || taskInfo.isUserFullscreenOverrideEnabled);
     }
+
+    private long getDisappearTimeMs() {
+        return mDisappearTimeSupplier.apply(AccessibilityManager.FLAG_CONTENT_CONTROLS);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 9472341..e28b8d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -24,6 +24,7 @@
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.view.IWindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.launcher3.icons.IconProvider;
@@ -233,10 +234,12 @@
             DisplayImeController imeController, SyncTransactionQueue syncQueue,
             @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy,
             DockStateReader dockStateReader, CompatUIConfiguration compatUIConfiguration,
-            CompatUIShellCommandHandler compatUIShellCommandHandler) {
+            CompatUIShellCommandHandler compatUIShellCommandHandler,
+            AccessibilityManager accessibilityManager) {
         return new CompatUIController(context, shellInit, shellController, displayController,
                 displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy,
-                dockStateReader, compatUIConfiguration, compatUIShellCommandHandler);
+                dockStateReader, compatUIConfiguration, compatUIShellCommandHandler,
+                accessibilityManager);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 24ef44a..4e92ca1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -34,6 +34,7 @@
 import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipMediaController;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.common.pip.SizeSpecSource;
 import com.android.wm.shell.dagger.WMShellBaseModule;
 import com.android.wm.shell.dagger.WMSingleton;
@@ -50,7 +51,6 @@
 import com.android.wm.shell.pip.PipTransition;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
 import com.android.wm.shell.pip.phone.PipController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
index 04032bb1..9c9364e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -18,9 +18,9 @@
 
 import android.annotation.Nullable;
 
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.dagger.WMSingleton;
 import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUtils;
 
 import dagger.Module;
 import dagger.Provides;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 633f627..b0f75c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -311,7 +311,7 @@
         )
         val wct = WindowContainerTransaction()
         wct.setWindowingMode(task.token, WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW)
-        wct.setBounds(task.token, null)
+        wct.setBounds(task.token, Rect())
         wct.setDensityDpi(task.token, getDefaultDensityDpi())
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index ac711ea..4fef672 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -28,6 +28,7 @@
 import android.view.Gravity;
 
 import com.android.wm.shell.R;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.common.pip.SizeSpecSource;
 
 import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
index 456f85b..4aa260b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.pip;
 
-import static com.android.wm.shell.pip.PipUtils.dpToPx;
+import static com.android.wm.shell.common.pip.PipUtils.dpToPx;
 
 import android.content.Context;
 import android.content.res.Resources;
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 296857b..ed9ff1c 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
@@ -83,6 +83,7 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index db7e2c0..83e03dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -64,6 +64,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 0f74f9e..64bba67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -38,6 +38,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
deleted file mode 100644
index 3cd9848..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.pip;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
-import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.Pair;
-import android.util.TypedValue;
-import android.window.TaskSnapshot;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
-
-import java.util.List;
-import java.util.Objects;
-
-/** A class that includes convenience methods. */
-public class PipUtils {
-    private static final String TAG = "PipUtils";
-
-    // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal.
-    private static final double EPSILON = 1e-7;
-
-    private static final String ENABLE_PIP2_IMPLEMENTATION =
-            "persist.wm.debug.enable_pip2_implementation";
-
-    /**
-     * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack.
-     * The component name may be null if no such activity exists.
-     */
-    public static Pair<ComponentName, Integer> getTopPipActivity(Context context) {
-        try {
-            final String sysUiPackageName = context.getPackageName();
-            final RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
-                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-            if (pinnedTaskInfo != null && pinnedTaskInfo.childTaskIds != null
-                    && pinnedTaskInfo.childTaskIds.length > 0) {
-                for (int i = pinnedTaskInfo.childTaskNames.length - 1; i >= 0; i--) {
-                    ComponentName cn = ComponentName.unflattenFromString(
-                            pinnedTaskInfo.childTaskNames[i]);
-                    if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) {
-                        return new Pair<>(cn, pinnedTaskInfo.childTaskUserIds[i]);
-                    }
-                }
-            }
-        } catch (RemoteException e) {
-            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                    "%s: Unable to get pinned stack.", TAG);
-        }
-        return new Pair<>(null, 0);
-    }
-
-    /**
-     * @return the pixels for a given dp value.
-     */
-    public static int dpToPx(float dpValue, DisplayMetrics dm) {
-        return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
-    }
-
-    /**
-     * @return true if the aspect ratios differ
-     */
-    public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
-        return Math.abs(aspectRatio1 - aspectRatio2) > EPSILON;
-    }
-
-    /**
-     * Checks whether title, description and intent match.
-     * Comparing icons would be good, but using equals causes false negatives
-     */
-    public static boolean remoteActionsMatch(RemoteAction action1, RemoteAction action2) {
-        if (action1 == action2) return true;
-        if (action1 == null || action2 == null) return false;
-        return action1.isEnabled() == action2.isEnabled()
-                && action1.shouldShowIcon() == action2.shouldShowIcon()
-                && Objects.equals(action1.getTitle(), action2.getTitle())
-                && Objects.equals(action1.getContentDescription(), action2.getContentDescription())
-                && Objects.equals(action1.getActionIntent(), action2.getActionIntent());
-    }
-
-    /**
-     * Returns true if the actions in the lists match each other according to {@link
-     * PipUtils#remoteActionsMatch(RemoteAction, RemoteAction)}, including their position.
-     */
-    public static boolean remoteActionsChanged(List<RemoteAction> list1, List<RemoteAction> list2) {
-        if (list1 == null && list2 == null) {
-            return false;
-        }
-        if (list1 == null || list2 == null) {
-            return true;
-        }
-        if (list1.size() != list2.size()) {
-            return true;
-        }
-        for (int i = 0; i < list1.size(); i++) {
-            if (!remoteActionsMatch(list1.get(i), list2.get(i))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /** @return {@link TaskSnapshot} for a given task id. */
-    @Nullable
-    public static TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
-        if (taskId <= 0) return null;
-        try {
-            return ActivityTaskManager.getService().getTaskSnapshot(
-                    taskId, isLowResolution, false /* takeSnapshotIfNeeded */);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
-            return null;
-        }
-    }
-
-    public static boolean isPip2ExperimentEnabled() {
-        return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false);
-    }
-}
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 5c65d78..ddea574 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
@@ -77,6 +77,7 @@
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.pip.IPip;
@@ -93,7 +94,6 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
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 837426a..fc34772 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
@@ -67,7 +67,7 @@
 import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
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 6055fd9..cf54a71 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
@@ -51,13 +51,13 @@
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.common.pip.SizeSpecSource;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.sysui.ShellInit;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
index f1606f6..6b890c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipActionsProvider.java
@@ -36,7 +36,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.pip.PipMediaController;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 7c15637..57439a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -55,7 +55,7 @@
 import com.android.internal.widget.RecyclerView;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.TvWindowMenuActionButton;
-import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 39c7a4b..1c94625 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -37,8 +37,8 @@
 import com.android.internal.util.ImageUtils;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
 import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index dbec607..6720804 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -26,6 +26,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
@@ -36,7 +37,6 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.util.Objects;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
new file mode 100644
index 0000000..ec09827
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
@@ -0,0 +1,3 @@
+# WM shell sub-module pip owner
+hwwang@google.com
+mateuszc@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 7df658e..d310ae3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -37,8 +37,12 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
 import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
@@ -334,6 +338,10 @@
         boolean isDisplayRotationAnimationStarted = false;
         final boolean isDreamTransition = isDreamTransition(info);
         final boolean isOnlyTranslucent = isOnlyTranslucent(info);
+        final boolean isActivityReplace = checkActivityReplacement(info, startTransaction);
+        // Some patterns (eg. activity "replacement") require us to re-interpret the type
+        @WindowManager.TransitionType final int transitType =
+                isActivityReplace ? TRANSIT_OPEN : info.getType();
 
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
@@ -430,7 +438,8 @@
             // Don't animate anything that isn't independent.
             if (!TransitionInfo.isIndependent(change, info)) continue;
 
-            Animation a = loadAnimation(info, change, wallpaperTransit, isDreamTransition);
+            Animation a = loadAnimation(transitType, info, change, wallpaperTransit,
+                    isDreamTransition);
             if (a != null) {
                 if (isTask) {
                     final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
@@ -604,6 +613,53 @@
         return (translucentOpen + translucentClose) > 0;
     }
 
+    /**
+     * Checks for an edge-case where an activity calls finish() followed immediately by
+     * startActivity() to "replace" itself. If in this case, it will swap the layer of the
+     * close/open activities and return `true`. This way, we pretend like we are just "opening"
+     * the new activity.
+     */
+    private static boolean checkActivityReplacement(@NonNull TransitionInfo info,
+            SurfaceControl.Transaction t) {
+        if (info.getType() != TRANSIT_CLOSE) {
+            return false;
+        }
+        int closing = -1;
+        int opening = -1;
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+            if ((change.getTaskInfo() != null || change.hasFlags(FLAG_IS_DISPLAY))
+                    && !TransitionUtil.isOrderOnly(change)) {
+                // This isn't an activity-level transition.
+                return false;
+            }
+            if (change.getTaskInfo() != null
+                    && change.hasFlags(FLAG_IS_DISPLAY | FLAGS_IS_NON_APP_WINDOW)) {
+                // Ignore non-activity containers.
+                continue;
+            }
+            if (TransitionUtil.isClosingType(change.getMode())) {
+                closing = i;
+            } else if (change.getMode() == TRANSIT_OPEN) {
+                // OPEN implies that it is a new launch. If going "back" the opening app will be
+                // TO_FRONT
+                opening = i;
+            } else if (change.getMode() == TRANSIT_TO_FRONT) {
+                // Normal "going back", so not a replacement.
+                return false;
+            }
+        }
+        if (closing < 0 || opening < 0) {
+            return false;
+        }
+        // Swap the opening and closing z-orders since we're swapping the transit type.
+        final int numChanges = info.getChanges().size();
+        final int zSplitLine = numChanges + 1;
+        t.setLayer(info.getChanges().get(opening).getLeash(), zSplitLine + numChanges - opening);
+        t.setLayer(info.getChanges().get(closing).getLeash(), zSplitLine - closing);
+        return true;
+    }
+
     @Override
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@@ -656,12 +712,11 @@
     }
 
     @Nullable
-    private Animation loadAnimation(@NonNull TransitionInfo info,
+    private Animation loadAnimation(int type, @NonNull TransitionInfo info,
             @NonNull TransitionInfo.Change change, int wallpaperTransit,
             boolean isDreamTransition) {
         Animation a;
 
-        final int type = info.getType();
         final int flags = info.getFlags();
         final int changeMode = change.getMode();
         final int changeFlags = change.getFlags();
@@ -716,8 +771,8 @@
             // If there's a scene-transition, then jump-cut.
             return null;
         } else {
-            a = loadAttributeAnimation(
-                    info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
+            a = loadAttributeAnimation(type, info, change, wallpaperTransit, mTransitionAnimation,
+                    isDreamTransition);
         }
 
         if (a != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index d07d2b7b6..c99911d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -45,6 +45,7 @@
 import android.graphics.Shader;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.view.WindowManager;
 import android.view.animation.Animation;
 import android.view.animation.Transformation;
 import android.window.ScreenCapture;
@@ -61,10 +62,10 @@
 
     /** Loads the animation that is defined through attribute id for the given transition. */
     @Nullable
-    public static Animation loadAttributeAnimation(@NonNull TransitionInfo info,
-            @NonNull TransitionInfo.Change change, int wallpaperTransit,
-            @NonNull TransitionAnimation transitionAnimation, boolean isDreamTransition) {
-        final int type = info.getType();
+    public static Animation loadAttributeAnimation(@WindowManager.TransitionType int type,
+            @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
+            int wallpaperTransit, @NonNull TransitionAnimation transitionAnimation,
+            boolean isDreamTransition) {
         final int changeMode = change.getMode();
         final int changeFlags = change.getFlags();
         final boolean enter = TransitionUtil.isOpeningType(changeMode);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
index 367676f..f7a060f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
@@ -46,5 +46,7 @@
         default void onStateChangeProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
 
         default void onStateChangeFinished() {}
+
+        default void onFoldStateChanged(boolean isFolded) {}
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 2eb6e71..68b5a81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -63,6 +63,7 @@
     @Nullable
     private IBinder mTransition;
 
+    private boolean mAnimationFinished = false;
     private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
 
     public UnfoldTransitionHandler(ShellInit shellInit,
@@ -132,6 +133,13 @@
 
         startTransaction.apply();
         mFinishCallback = finishCallback;
+
+        // Shell transition started when unfold animation has already finished,
+        // finish shell transition immediately
+        if (mAnimationFinished) {
+            finishTransitionIfNeeded();
+        }
+
         return true;
     }
 
@@ -161,17 +169,8 @@
 
     @Override
     public void onStateChangeFinished() {
-        if (mFinishCallback == null) return;
-
-        for (int i = 0; i < mAnimators.size(); i++) {
-            final UnfoldTaskAnimator animator = mAnimators.get(i);
-            animator.clearTasks();
-            animator.stop();
-        }
-
-        mFinishCallback.onTransitionFinished(null);
-        mFinishCallback = null;
-        mTransition = null;
+        mAnimationFinished = true;
+        finishTransitionIfNeeded();
     }
 
     @Override
@@ -218,4 +217,25 @@
     public boolean willHandleTransition() {
         return mTransition != null;
     }
+
+    @Override
+    public void onFoldStateChanged(boolean isFolded) {
+        if (isFolded) {
+            mAnimationFinished = false;
+        }
+    }
+
+    private void finishTransitionIfNeeded() {
+        if (mFinishCallback == null) return;
+
+        for (int i = 0; i < mAnimators.size(); i++) {
+            final UnfoldTaskAnimator animator = mAnimators.get(i);
+            animator.clearTasks();
+            animator.stop();
+        }
+
+        mFinishCallback.onTransitionFinished(null);
+        mFinishCallback = null;
+        mTransition = null;
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 97147a3..bc095bb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -31,6 +31,7 @@
 import androidx.test.uiautomator.UiObject2
 import androidx.test.uiautomator.Until
 import com.android.server.wm.flicker.helpers.LaunchBubbleHelper
+import com.android.server.wm.flicker.helpers.MultiWindowUtils
 import com.android.wm.shell.flicker.BaseTest
 import org.junit.runners.Parameterized
 
@@ -56,6 +57,9 @@
     ): FlickerBuilder.() -> Unit {
         return {
             setup {
+                MultiWindowUtils.executeShellCommand(
+                        instrumentation,
+                        "settings put secure force_hide_bubbles_user_education 1")
                 notifyManager.setBubblesAllowed(
                     testApp.packageName,
                     uid,
@@ -67,6 +71,9 @@
             }
 
             teardown {
+                MultiWindowUtils.executeShellCommand(
+                        instrumentation,
+                        "settings put secure force_hide_bubbles_user_education 0")
                 notifyManager.setBubblesAllowed(
                     testApp.packageName,
                     uid,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
index 139724f..58d9a64 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
@@ -203,6 +203,60 @@
         assertThat(restingPosition.y).isEqualTo(getDefaultYPosition());
     }
 
+    /** Test that the default resting position on tablet is middle right. */
+    @Test
+    public void testGetDefaultPosition_appBubble_onTablet() {
+        new WindowManagerConfig().setLargeScreen().setUpConfig();
+        mPositioner.update();
+
+        RectF allowableStackRegion =
+                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
+        PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+
+        assertThat(startPosition.x).isEqualTo(allowableStackRegion.right);
+        assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
+    }
+
+    @Test
+    public void testGetRestingPosition_appBubble_onTablet_RTL() {
+        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+                LAYOUT_DIRECTION_RTL).setUpConfig();
+        mPositioner.update();
+
+        RectF allowableStackRegion =
+                mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */);
+        PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */);
+
+        assertThat(startPosition.x).isEqualTo(allowableStackRegion.left);
+        assertThat(startPosition.y).isEqualTo(getDefaultYPosition());
+    }
+
+    @Test
+    public void testHasUserModifiedDefaultPosition_false() {
+        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+                LAYOUT_DIRECTION_RTL).setUpConfig();
+        mPositioner.update();
+
+        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+
+        mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition());
+
+        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+    }
+
+    @Test
+    public void testHasUserModifiedDefaultPosition_true() {
+        new WindowManagerConfig().setLargeScreen().setLayoutDirection(
+                LAYOUT_DIRECTION_RTL).setUpConfig();
+        mPositioner.update();
+
+        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse();
+
+        mPositioner.setRestingPosition(new PointF(0, 100));
+
+        assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue();
+    }
+
     /**
      * Calculates the Y position bubbles should be placed based on the config. Based on
      * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index efc69ebd..9b9600e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -41,6 +41,7 @@
 import android.testing.AndroidTestingRunner;
 import android.view.InsetsSource;
 import android.view.InsetsState;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -113,6 +114,9 @@
     @Mock
     private CompatUIShellCommandHandler mCompatUIShellCommandHandler;
 
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
 
@@ -139,7 +143,7 @@
         mController = new CompatUIController(mContext, mShellInit, mMockShellController,
                 mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
                 mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
-                mCompatUIConfiguration, mCompatUIShellCommandHandler) {
+                mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
index a802f15a..5867a85 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/ReachabilityEduWindowManagerTest.java
@@ -109,6 +109,6 @@
     private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) {
         return new ReachabilityEduWindowManager(mContext, taskInfo, mSyncTransactionQueue,
                 mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor,
-                mOnDismissCallback);
+                mOnDismissCallback, flags -> 0);
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index 1fee153..ce1290b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -93,7 +93,7 @@
         mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
                 mSyncTransactionQueue, mTaskListener, new DisplayLayout(),
                 new CompatUIController.CompatUIHintsState(),
-                mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor());
+                mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0);
 
         mLayout = (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate(
                 R.layout.user_aspect_ratio_settings_layout, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index b48538c..08cc2f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -106,7 +106,7 @@
                 false, /* topActivityBoundsLetterboxed */ true);
         mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
                 mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
-                mOnUserAspectRatioSettingsButtonClicked, mExecutor);
+                mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0);
         spyOn(mWindowManager);
         doReturn(mLayout).when(mWindowManager).inflateLayout();
         doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 99a1ac6..a57a7bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -99,6 +99,7 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.recents.RecentsTransitionHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -1061,7 +1062,8 @@
                         mTransactionPool, createTestDisplayController(), mMainExecutor,
                         mMainHandler, mAnimExecutor);
         final RecentsTransitionHandler recentsHandler =
-                new RecentsTransitionHandler(shellInit, transitions, null);
+                new RecentsTransitionHandler(shellInit, transitions,
+                        mock(RecentTasksController.class));
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
         shellInit.init();
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
new file mode 100644
index 0000000..63a685e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.unfold;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
+import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
+import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class UnfoldTransitionHandlerTest {
+
+    private UnfoldTransitionHandler mUnfoldTransitionHandler;
+
+    private final TestShellUnfoldProgressProvider mShellUnfoldProgressProvider =
+            new TestShellUnfoldProgressProvider();
+    private final TestTransactionPool mTransactionPool = new TestTransactionPool();
+
+    private FullscreenUnfoldTaskAnimator mFullscreenUnfoldTaskAnimator;
+    private SplitTaskUnfoldAnimator mSplitTaskUnfoldAnimator;
+    private Transitions mTransitions;
+
+    private final IBinder mTransition = new Binder();
+
+    @Before
+    public void before() {
+        final ShellExecutor executor = new TestSyncExecutor();
+        final ShellInit shellInit = new ShellInit(executor);
+
+        mFullscreenUnfoldTaskAnimator = mock(FullscreenUnfoldTaskAnimator.class);
+        mSplitTaskUnfoldAnimator = mock(SplitTaskUnfoldAnimator.class);
+        mTransitions = mock(Transitions.class);
+
+        mUnfoldTransitionHandler = new UnfoldTransitionHandler(
+                shellInit,
+                mShellUnfoldProgressProvider,
+                mFullscreenUnfoldTaskAnimator,
+                mSplitTaskUnfoldAnimator,
+                mTransactionPool,
+                executor,
+                mTransitions
+        );
+
+        shellInit.init();
+    }
+
+    @Test
+    public void handleRequest_physicalDisplayChange_handlesTransition() {
+        ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
+        TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
+                Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
+        TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
+                triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+
+        WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition,
+                requestInfo);
+
+        assertThat(result).isNotNull();
+    }
+
+    @Test
+    public void handleRequest_noPhysicalDisplayChange_doesNotHandleTransition() {
+        ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
+        TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
+                Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(false);
+        TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
+                triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+
+        WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition,
+                requestInfo);
+
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() {
+        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+        mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                mock(TransitionInfo.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+
+        verify(finishCallback, never()).onTransitionFinished(any());
+    }
+
+    @Test
+    public void startAnimation_animationFinishes_finishesTheTransition() {
+        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+        mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                mock(TransitionInfo.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+        mShellUnfoldProgressProvider.onStateChangeStarted();
+        mShellUnfoldProgressProvider.onStateChangeFinished();
+
+        verify(finishCallback).onTransitionFinished(any());
+    }
+
+    @Test
+    public void startAnimation_animationIsAlreadyFinished_finishesTheTransition() {
+        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+        mShellUnfoldProgressProvider.onStateChangeStarted();
+        mShellUnfoldProgressProvider.onStateChangeFinished();
+        mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                mock(TransitionInfo.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+
+        verify(finishCallback).onTransitionFinished(any());
+    }
+
+    @Test
+    public void startAnimationSecondTimeAfterFold_animationAlreadyFinished_finishesTransition() {
+        TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+        TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
+
+        // First unfold
+        mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ false);
+        mShellUnfoldProgressProvider.onStateChangeStarted();
+        mShellUnfoldProgressProvider.onStateChangeFinished();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+        mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                mock(TransitionInfo.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+        clearInvocations(finishCallback);
+
+        // Fold
+        mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ true);
+
+        // Second unfold
+        mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ false);
+        mShellUnfoldProgressProvider.onStateChangeStarted();
+        mShellUnfoldProgressProvider.onStateChangeFinished();
+        mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
+        mUnfoldTransitionHandler.startAnimation(
+                mTransition,
+                mock(TransitionInfo.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                finishCallback
+        );
+
+        verify(finishCallback).onTransitionFinished(any());
+    }
+
+    private TransitionRequestInfo createUnfoldTransitionRequestInfo() {
+        ActivityManager.RunningTaskInfo triggerTaskInfo = new ActivityManager.RunningTaskInfo();
+        TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
+                Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
+        return new TransitionRequestInfo(TRANSIT_CHANGE,
+                triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+    }
+
+    private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
+            ShellUnfoldProgressProvider.UnfoldListener {
+
+        private final List<UnfoldListener> mListeners = new ArrayList<>();
+
+        @Override
+        public void addListener(Executor executor, UnfoldListener listener) {
+            mListeners.add(listener);
+        }
+
+        @Override
+        public void onFoldStateChanged(boolean isFolded) {
+            mListeners.forEach(unfoldListener -> unfoldListener.onFoldStateChanged(isFolded));
+        }
+
+        @Override
+        public void onStateChangeFinished() {
+            mListeners.forEach(UnfoldListener::onStateChangeFinished);
+        }
+
+        @Override
+        public void onStateChangeProgress(float progress) {
+            mListeners.forEach(unfoldListener -> unfoldListener.onStateChangeProgress(progress));
+        }
+
+        @Override
+        public void onStateChangeStarted() {
+            mListeners.forEach(UnfoldListener::onStateChangeStarted);
+        }
+    }
+
+    private static class TestTransactionPool extends TransactionPool {
+        @Override
+        public SurfaceControl.Transaction acquire() {
+            return mock(SurfaceControl.Transaction.class);
+        }
+
+        @Override
+        public void release(SurfaceControl.Transaction t) {
+        }
+    }
+
+    private static class TestSyncExecutor implements ShellExecutor {
+        @Override
+        public void execute(Runnable runnable) {
+            runnable.run();
+        }
+
+        @Override
+        public void executeDelayed(Runnable runnable, long delayMillis) {
+            runnable.run();
+        }
+
+        @Override
+        public void removeCallbacks(Runnable runnable) {
+        }
+
+        @Override
+        public boolean hasCallback(Runnable runnable) {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/location/Android.bp b/location/Android.bp
new file mode 100644
index 0000000..46dca74
--- /dev/null
+++ b/location/Android.bp
@@ -0,0 +1,41 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+//location sources that will populate the new module
+filegroup {
+    name: "framework-location-nonupdatable-sources",
+    srcs: [
+        "placeholder_java/android/location/Placeholder.java",
+    ],
+}
+
+java_sdk_library {
+    name: "framework-location",
+    srcs: [
+        ":framework-location-nonupdatable-sources",
+    ],
+    defaults: ["framework-non-updatable-unbundled-defaults"],
+    permitted_packages: [
+        "android.location",
+        "com.android.internal.location",
+    ],
+    libs: [
+        "app-compat-annotations",
+        "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+    ],
+    hidden_api_packages: [
+        "com.android.internal.location",
+    ],
+    aidl: {
+        include_dirs: [
+            "frameworks/base/location/java",
+            "frameworks/base/core/java",
+        ],
+    },
+}
diff --git a/location/api/current.txt b/location/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-current.txt b/location/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-removed.txt b/location/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/removed.txt b/location/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-removed.txt b/location/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-current.txt b/location/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-removed.txt b/location/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/location/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/java/android/location/GnssRequest.java b/location/java/android/location/GnssRequest.java
index 9c9766f..f3a40cc 100644
--- a/location/java/android/location/GnssRequest.java
+++ b/location/java/android/location/GnssRequest.java
@@ -41,7 +41,7 @@
      *
      * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
      * discontinuities are expected, and when supported, carrier phase should be continuous in
-     * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+     * good signal conditions. All non-denylisted, healthy constellations, satellites and
      * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
      * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
      * duty cycling, constellations and frequency limits, etc.
@@ -138,7 +138,7 @@
          *
          * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
          * discontinuities are expected, and when supported, carrier phase should be continuous in
-         * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+         * good signal conditions. All non-denylisted, healthy constellations, satellites and
          * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
          * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
          * duty cycling, constellations and frequency limits, etc.
diff --git a/location/placeholder_java/android/location/Placeholder.java b/location/placeholder_java/android/location/Placeholder.java
new file mode 100644
index 0000000..f0dbce8
--- /dev/null
+++ b/location/placeholder_java/android/location/Placeholder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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.location;
+
+/**
+ * Placeholder class so new frameworks-location module isn't empty, will be removed once module is
+ * populated.
+ *
+ * @hide
+ *
+ */
+public class Placeholder {
+}
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 5ae77b5..a9da832 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -37,6 +37,17 @@
         }
       ],
       "file_patterns": ["(?i)drm|crypto"]
+    },
+    {
+      "file_patterns": [
+        "[^/]*(Ringtone)[^/]*\\.java"
+      ],
+      "name": "MediaRingtoneTests",
+      "options": [
+        {"exclude-annotation": "androidx.test.filters.LargeTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
     }
   ]
 }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 0ff1b1e..9234479 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -35,19 +36,20 @@
 import android.database.Cursor;
 import android.database.StaleDataException;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.vibrator.persistence.VibrationXmlParser;
 import android.provider.BaseColumns;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio.AudioColumns;
 import android.provider.MediaStore.MediaColumns;
 import android.provider.Settings;
 import android.provider.Settings.System;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.database.SortCursor;
@@ -58,6 +60,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -209,21 +213,30 @@
      */
     public static final String EXTRA_RINGTONE_PICKED_URI =
             "android.intent.extra.ringtone.PICKED_URI";
-    
+
+    /**
+     * Declares the allowed types of media for this RingtoneManager.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "MEDIA_", value = {
+            Ringtone.MEDIA_SOUND,
+            Ringtone.MEDIA_VIBRATION,
+    })
+    public @interface MediaType {}
+
     // Make sure the column ordering and then ..._COLUMN_INDEX are in sync
     
-    private static final String[] INTERNAL_COLUMNS = new String[] {
+    private static final String[] MEDIA_AUDIO_COLUMNS = new String[] {
         MediaStore.Audio.Media._ID,
         MediaStore.Audio.Media.TITLE,
         MediaStore.Audio.Media.TITLE,
         MediaStore.Audio.Media.TITLE_KEY,
     };
 
-    private static final String[] MEDIA_COLUMNS = new String[] {
-        MediaStore.Audio.Media._ID,
-        MediaStore.Audio.Media.TITLE,
-        MediaStore.Audio.Media.TITLE,
-        MediaStore.Audio.Media.TITLE_KEY,
+    private static final String[] MEDIA_VIBRATION_COLUMNS = new String[]{
+            MediaStore.Files.FileColumns._ID,
+            MediaStore.Files.FileColumns.TITLE,
     };
 
     /**
@@ -251,7 +264,9 @@
     private Cursor mCursor;
 
     private int mType = TYPE_RINGTONE;
-    
+    @MediaType
+    private int mMediaType = Ringtone.MEDIA_SOUND;
+
     /**
      * If a column (item from this list) exists in the Cursor, its value must
      * be true (value of 1) for the row to be returned.
@@ -318,6 +333,41 @@
     }
 
     /**
+     * Sets the media type that will be listed by the RingtoneManager.
+     *
+     * <p>This method should be called before calling {@link RingtoneManager#getCursor()}.
+     *
+     * @hide
+     */
+    public void setMediaType(@MediaType int mediaType) {
+        if (mCursor != null) {
+            throw new IllegalStateException(
+                    "Setting media should be done before calling getCursor().");
+        }
+
+        switch (mediaType) {
+            case Ringtone.MEDIA_SOUND:
+            case Ringtone.MEDIA_VIBRATION:
+                mMediaType = mediaType;
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported media type " + mediaType);
+        }
+    }
+
+    /**
+     * Returns the RingtoneManagers media type.
+     *
+     * @return the media type.
+     * @see #setMediaType
+     * @hide
+     */
+    @MediaType
+    public int getMediaType() {
+        return mMediaType;
+    }
+
+    /**
      * Sets which type(s) of ringtones will be listed by this.
      * 
      * @param type The type(s), one or more of {@link #TYPE_RINGTONE},
@@ -454,19 +504,19 @@
             return mCursor;
         }
 
-        ArrayList<Cursor> ringtoneCursors = new ArrayList<Cursor>();
-        ringtoneCursors.add(getInternalRingtones());
-        ringtoneCursors.add(getMediaRingtones());
+        ArrayList<Cursor> cursors = new ArrayList<>();
+
+        cursors.add(queryMediaStore(/* internal= */ true));
+        cursors.add(queryMediaStore(/* internal= */ false));
 
         if (mIncludeParentRingtones) {
             Cursor parentRingtonesCursor = getParentProfileRingtones();
             if (parentRingtonesCursor != null) {
-                ringtoneCursors.add(parentRingtonesCursor);
+                cursors.add(parentRingtonesCursor);
             }
         }
-
-        return mCursor = new SortCursor(ringtoneCursors.toArray(new Cursor[ringtoneCursors.size()]),
-                MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
+        return mCursor = new SortCursor(cursors.toArray(new Cursor[cursors.size()]),
+                getSortOrderForMedia(mMediaType));
     }
 
     private Cursor getParentProfileRingtones() {
@@ -478,9 +528,7 @@
                 // We don't need to re-add the internal ringtones for the work profile since
                 // they are the same as the personal profile. We just need the external
                 // ringtones.
-                final Cursor res = getMediaRingtones(parentContext);
-                return new ExternalRingtonesCursorWrapper(res, ContentProvider.maybeAddUserId(
-                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, parentInfo.id));
+                return queryMediaStore(parentContext, /* internal= */ false);
             }
         }
         return null;
@@ -502,7 +550,7 @@
         Uri positionUri = getRingtoneUri(position);
         if (Ringtone.useRingtoneV2()) {
             mPreviousRingtone = new Ringtone.Builder(
-                    mContext, Ringtone.MEDIA_SOUND, getDefaultAudioAttributes(mType))
+                    mContext, mMediaType, getDefaultAudioAttributes(mType))
                     .setUri(positionUri)
                     .build();
         } else {
@@ -675,11 +723,13 @@
      */
     public static Uri getValidRingtoneUri(Context context) {
         final RingtoneManager rm = new RingtoneManager(context);
-        
-        Uri uri = getValidRingtoneUriFromCursorAndClose(context, rm.getInternalRingtones());
+
+        Uri uri = getValidRingtoneUriFromCursorAndClose(context,
+                rm.queryMediaStore(/* internal= */ true));
 
         if (uri == null) {
-            uri = getValidRingtoneUriFromCursorAndClose(context, rm.getMediaRingtones());
+            uri = getValidRingtoneUriFromCursorAndClose(context,
+                    rm.queryMediaStore(/* internal= */ false));
         }
         
         return uri;
@@ -700,28 +750,26 @@
         }
     }
 
-    @UnsupportedAppUsage
-    private Cursor getInternalRingtones() {
-        final Cursor res = query(
-                MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
-                constructBooleanTrueWhereClause(mFilterColumns),
-                null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
-        return new ExternalRingtonesCursorWrapper(res, MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
+    private Cursor queryMediaStore(boolean internal) {
+        return queryMediaStore(mContext, internal);
     }
 
-    private Cursor getMediaRingtones() {
-        final Cursor res = getMediaRingtones(mContext);
-        return new ExternalRingtonesCursorWrapper(res, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
-    }
+    private Cursor queryMediaStore(Context context, boolean internal) {
+        Uri contentUri = getContentUriForMedia(mMediaType, internal);
+        String[] columns =
+                mMediaType == Ringtone.MEDIA_VIBRATION ? MEDIA_VIBRATION_COLUMNS
+                        : MEDIA_AUDIO_COLUMNS;
+        String whereClause = getWhereClauseForMedia(mMediaType, mFilterColumns);
+        String sortOrder = getSortOrderForMedia(mMediaType);
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private Cursor getMediaRingtones(Context context) {
-        // MediaStore now returns ringtones on other storage devices, even when
-        // we don't have storage or audio permissions
-        return query(
-                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
-                constructBooleanTrueWhereClause(mFilterColumns), null,
-                MediaStore.Audio.Media.DEFAULT_SORT_ORDER, context);
+        Cursor cursor = query(contentUri, columns, whereClause, /* selectionArgs= */ null,
+                sortOrder, context);
+
+        if (context.getUserId() != mContext.getUserId()) {
+            contentUri = ContentProvider.maybeAddUserId(contentUri, context.getUserId());
+        }
+
+        return new ExternalRingtonesCursorWrapper(cursor, contentUri);
     }
 
     private void setFilterColumnsList(int type) {
@@ -740,6 +788,56 @@
             columns.add(MediaStore.Audio.AudioColumns.IS_ALARM);
         }
     }
+
+    /**
+     * Returns the sort order for the specified media.
+     *
+     * @param media The RingtoneManager media type.
+     * @return The sort order column.
+     */
+    private static String getSortOrderForMedia(@MediaType int media) {
+        return media == Ringtone.MEDIA_VIBRATION ? MediaStore.Files.FileColumns.TITLE
+                : MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
+    }
+
+    /**
+     * Returns the content URI based on the specified media and whether it's internal or external
+     * storage.
+     *
+     * @param media    The RingtoneManager media type.
+     * @param internal Whether it's for internal or external storage.
+     * @return The media content URI.
+     */
+    private static Uri getContentUriForMedia(@MediaType int media, boolean internal) {
+        switch (media) {
+            case Ringtone.MEDIA_VIBRATION:
+                return MediaStore.Files.getContentUri(
+                        internal ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL);
+            case Ringtone.MEDIA_SOUND:
+                return internal ? MediaStore.Audio.Media.INTERNAL_CONTENT_URI
+                        : MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+            default:
+                throw new IllegalArgumentException("Unsupported media type " + media);
+        }
+    }
+
+    /**
+     * Constructs a where clause based on the media type. This will be used to find all matching
+     * sound or vibration files.
+     *
+     * @param media   The RingtoneManager media type.
+     * @param columns The columns that must be true, when media type is {@link Ringtone#MEDIA_SOUND}
+     * @return The where clause.
+     */
+    private static String getWhereClauseForMedia(@MediaType int media, List<String> columns) {
+        // TODO(b/296213309): Filtering by ringtone-type isn't supported yet for vibrations.
+        if (media == Ringtone.MEDIA_VIBRATION) {
+            return TextUtils.formatSimple("(%s='%s')", MediaStore.Files.FileColumns.MIME_TYPE,
+                    VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE);
+        }
+
+        return constructBooleanTrueWhereClause(columns);
+    }
     
     /**
      * Constructs a where clause that consists of at least one column being 1
@@ -769,14 +867,6 @@
 
         return sb.toString();
     }
-    
-    private Cursor query(Uri uri,
-            String[] projection,
-            String selection,
-            String[] selectionArgs,
-            String sortOrder) {
-        return query(uri, projection, selection, selectionArgs, sortOrder, mContext);
-    }
 
     private Cursor query(Uri uri,
             String[] projection,
diff --git a/media/tests/ringtone/Android.bp b/media/tests/ringtone/Android.bp
new file mode 100644
index 0000000..55b98c4
--- /dev/null
+++ b/media/tests/ringtone/Android.bp
@@ -0,0 +1,30 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "MediaRingtoneTests",
+
+    srcs: ["src/**/*.java"],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        "testng",
+        "androidx.test.ext.truth",
+        "frameworks-base-testutils",
+    ],
+
+    test_suites: [
+        "device-tests",
+        "automotive-tests",
+    ],
+
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/media/tests/ringtone/AndroidManifest.xml b/media/tests/ringtone/AndroidManifest.xml
new file mode 100644
index 0000000..27eda07
--- /dev/null
+++ b/media/tests/ringtone/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.framework.base.media.ringtone.tests">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="MediaRingtoneTests"
+                  android:label="Media Ringtone Tests"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.framework.base.media.ringtone.tests"
+                     android:label="Media Ringtone Tests"/>
+</manifest>
diff --git a/media/tests/ringtone/TEST_MAPPING b/media/tests/ringtone/TEST_MAPPING
new file mode 100644
index 0000000..6f25c14
--- /dev/null
+++ b/media/tests/ringtone/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+  "presubmit": [
+    {
+      "name": "MediaRingtoneTests",
+      "options": [
+        {"exclude-annotation": "androidx.test.filters.LargeTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "MediaRingtoneTests",
+      "options": [
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/media/tests/ringtone/res/raw/test_haptic_file.ahv b/media/tests/ringtone/res/raw/test_haptic_file.ahv
new file mode 100644
index 0000000..18c99c7
--- /dev/null
+++ b/media/tests/ringtone/res/raw/test_haptic_file.ahv
@@ -0,0 +1,17 @@
+<vibration>
+  <waveform-effect>
+    <waveform-entry durationMs="63" amplitude="255"/>
+    <waveform-entry durationMs="63" amplitude="231"/>
+    <waveform-entry durationMs="63" amplitude="208"/>
+    <waveform-entry durationMs="63" amplitude="185"/>
+    <waveform-entry durationMs="63" amplitude="162"/>
+    <waveform-entry durationMs="63" amplitude="139"/>
+    <waveform-entry durationMs="63" amplitude="115"/>
+    <waveform-entry durationMs="63" amplitude="92"/>
+    <waveform-entry durationMs="63" amplitude="69"/>
+    <waveform-entry durationMs="63" amplitude="46"/>
+    <waveform-entry durationMs="63" amplitude="23"/>
+    <waveform-entry durationMs="63" amplitude="0"/>
+    <waveform-entry durationMs="1250" amplitude="0"/>
+  </waveform-effect>
+</vibration>
diff --git a/media/tests/ringtone/res/raw/test_sound_file.mp3 b/media/tests/ringtone/res/raw/test_sound_file.mp3
new file mode 100644
index 0000000..c1b2fdf
--- /dev/null
+++ b/media/tests/ringtone/res/raw/test_sound_file.mp3
Binary files differ
diff --git a/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java b/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java
new file mode 100644
index 0000000..a92b298
--- /dev/null
+++ b/media/tests/ringtone/src/com/android/media/RingtoneManagerTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2023 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.media;
+
+import static com.google.android.mms.ContentType.AUDIO_MP3;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.framework.base.media.ringtone.tests.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class RingtoneManagerTest {
+    @RingtoneManager.MediaType
+    private final int mMediaType;
+    private final List<Uri> mAddedFilesUri;
+    private Context mContext;
+    private RingtoneManager mRingtoneManager;
+    private long mTimestamp;
+
+    @Parameterized.Parameters(name = "media = {0}")
+    public static Iterable<?> data() {
+        return Arrays.asList(Ringtone.MEDIA_SOUND, Ringtone.MEDIA_VIBRATION);
+    }
+
+    public RingtoneManagerTest(@RingtoneManager.MediaType int mediaType) {
+        mMediaType = mediaType;
+        mAddedFilesUri = new ArrayList<>();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mTimestamp = SystemClock.uptimeMillis();
+        mRingtoneManager = new RingtoneManager(mContext);
+        mRingtoneManager.setMediaType(mMediaType);
+    }
+
+    @After
+    public void tearDown() {
+        // Clean up media store
+        for (Uri fileUri : mAddedFilesUri) {
+            mContext.getContentResolver().delete(fileUri, null);
+        }
+    }
+
+    @Test
+    public void testSetMediaType_withValidValue_setsMediaCorrectly() {
+        mRingtoneManager.setMediaType(mMediaType);
+        assertThat(mRingtoneManager.getMediaType()).isEqualTo(mMediaType);
+    }
+
+    @Test
+    public void testSetMediaType_withInvalidValue_throwsException() {
+        assertThrows(IllegalArgumentException.class, () -> mRingtoneManager.setMediaType(999));
+    }
+
+    @Test
+    public void testSetMediaType_afterCallingGetCursor_throwsException() {
+        mRingtoneManager.getCursor();
+        assertThrows(IllegalStateException.class, () -> mRingtoneManager.setMediaType(mMediaType));
+    }
+
+    @Test
+    public void testGetRingtone_ringtoneHasCorrectTitle() throws Exception {
+        String fileName = generateUniqueFileName("new_file");
+        Ringtone ringtone = addNewRingtoneToMediaStore(mRingtoneManager, fileName);
+
+        assertThat(ringtone.getTitle(mContext)).isEqualTo(fileName);
+    }
+
+    @Test
+    public void testGetRingtone_ringtoneCanBePlayedAndStopped() throws Exception {
+        //TODO(b/261571543) Remove this assumption once we support playing vibrations.
+        assumeTrue(mMediaType == Ringtone.MEDIA_SOUND);
+        String fileName = generateUniqueFileName("new_file");
+        Ringtone ringtone = addNewRingtoneToMediaStore(mRingtoneManager, fileName);
+
+        ringtone.play();
+        assertThat(ringtone.isPlaying()).isTrue();
+
+        ringtone.stop();
+        assertThat(ringtone.isPlaying()).isFalse();
+    }
+
+    @Test
+    public void testGetCursor_withDifferentMedia_returnsCorrectCursor() throws Exception {
+        RingtoneManager audioRingtoneManager = new RingtoneManager(mContext);
+        String audioFileName = generateUniqueFileName("ringtone");
+        addNewRingtoneToMediaStore(audioRingtoneManager, audioFileName);
+
+        RingtoneManager vibrationRingtoneManager = new RingtoneManager(mContext);
+        vibrationRingtoneManager.setMediaType(Ringtone.MEDIA_VIBRATION);
+        String vibrationFileName = generateUniqueFileName("vibration");
+        addNewRingtoneToMediaStore(vibrationRingtoneManager, vibrationFileName);
+
+        Cursor audioCursor = audioRingtoneManager.getCursor();
+        Cursor vibrationCursor = vibrationRingtoneManager.getCursor();
+
+        List<String> audioTitles = extractRecordTitles(audioCursor);
+        List<String> vibrationTitles = extractRecordTitles(vibrationCursor);
+
+        assertThat(audioTitles).contains(audioFileName);
+        assertThat(audioTitles).doesNotContain(vibrationFileName);
+
+        assertThat(vibrationTitles).contains(vibrationFileName);
+        assertThat(vibrationTitles).doesNotContain(audioFileName);
+    }
+
+    private List<String> extractRecordTitles(Cursor cursor) {
+        List<String> titles = new ArrayList<>();
+
+        if (cursor.moveToFirst()) {
+            do {
+                String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
+                titles.add(title);
+            } while (cursor.moveToNext());
+        }
+
+        return titles;
+    }
+
+    private Ringtone addNewRingtoneToMediaStore(RingtoneManager ringtoneManager, String fileName)
+            throws Exception {
+        Uri fileUri = ringtoneManager.getMediaType() == Ringtone.MEDIA_SOUND ? addAudioFile(
+                fileName) : addVibrationFile(fileName);
+        mAddedFilesUri.add(fileUri);
+
+        int ringtonePosition = ringtoneManager.getRingtonePosition(fileUri);
+        Ringtone ringtone = ringtoneManager.getRingtone(ringtonePosition);
+        // Validate this is the expected ringtone.
+        assertThat(ringtone.getUri()).isEqualTo(fileUri);
+        return ringtone;
+    }
+
+    private Uri addAudioFile(String fileName) throws Exception {
+        ContentResolver resolver = mContext.getContentResolver();
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(MediaStore.Audio.Media.DISPLAY_NAME, fileName + ".mp3");
+        contentValues.put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_RINGTONES);
+        contentValues.put(MediaStore.Audio.Media.MIME_TYPE, AUDIO_MP3);
+        contentValues.put(MediaStore.Audio.Media.TITLE, fileName);
+        contentValues.put(MediaStore.Audio.Media.IS_RINGTONE, 1);
+
+        Uri contentUri = resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                contentValues);
+        writeRawDataToFile(resolver, contentUri, R.raw.test_sound_file);
+
+        return resolver.canonicalizeOrElse(contentUri);
+    }
+
+    private Uri addVibrationFile(String fileName) throws Exception {
+        ContentResolver resolver = mContext.getContentResolver();
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName + ".ahv");
+        contentValues.put(MediaStore.Files.FileColumns.RELATIVE_PATH,
+                Environment.DIRECTORY_DOWNLOADS);
+        contentValues.put(MediaStore.Files.FileColumns.MIME_TYPE,
+                VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE);
+        contentValues.put(MediaStore.Files.FileColumns.TITLE, fileName);
+
+        Uri contentUri = resolver.insert(MediaStore.Files.getContentUri(MediaStore
+                .VOLUME_EXTERNAL), contentValues);
+        writeRawDataToFile(resolver, contentUri, R.raw.test_haptic_file);
+
+        return resolver.canonicalizeOrElse(contentUri);
+    }
+
+    private void writeRawDataToFile(ContentResolver resolver, Uri contentUri, int rawResource)
+            throws Exception {
+        try (ParcelFileDescriptor pfd =
+                     resolver.openFileDescriptor(contentUri, "w", null)) {
+            InputStream inputStream = mContext.getResources().openRawResource(rawResource);
+            FileOutputStream outputStream = new FileOutputStream(pfd.getFileDescriptor());
+            outputStream.write(inputStream.readAllBytes());
+
+            inputStream.close();
+            outputStream.flush();
+            outputStream.close();
+
+        } catch (Exception e) {
+            throw new Exception("Failed to write data to file", e);
+        }
+    }
+
+    private String generateUniqueFileName(String prefix) {
+        return TextUtils.formatSimple("%s_%d", prefix, mTimestamp);
+    }
+
+}
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index c571b74..c25df6e 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -144,8 +144,6 @@
     binder::Status ret =
             mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
     if (!ret.isOk() || !session) {
-        ALOGE("%s: PerformanceHint cannot create hint session. %s", __FUNCTION__,
-              ret.exceptionMessage().c_str());
         return nullptr;
     }
     return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
diff --git a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
index 5fa6137..562f7be 100644
--- a/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
+++ b/packages/EasterEgg/src/com/android/egg/quares/QuaresActivity.kt
@@ -293,7 +293,7 @@
         return correct
     }
 
-    override fun onDraw(canvas: Canvas?) {
+    override fun onDraw(canvas: Canvas) {
         super.onDraw(canvas)
         if (!showText) return
         canvas?.let {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index 8e0cf89..a428142 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -78,6 +78,7 @@
             packageManager.getInstalledModules(0)
                 .filter { it.isHidden }
                 .map { it.packageName }
+                .filterNotNull()
                 .toSet()
         }
         val hideWhenDisabledPackagesDeferred = async {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
index 69c6131..92fd0cd 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
@@ -82,7 +82,8 @@
         val packageInfo = getPackageInfoAsUser(packageName, PackageManager.GET_PERMISSIONS, userId)
         val index = packageInfo?.requestedPermissions?.indexOf(permission) ?: return false
         return index >= 0 &&
-            packageInfo.requestedPermissionsFlags[index].hasFlag(REQUESTED_PERMISSION_GRANTED)
+            checkNotNull(packageInfo.requestedPermissionsFlags)[index]
+                .hasFlag(REQUESTED_PERMISSION_GRANTED)
     }
 
     override suspend fun getAppOpPermissionPackages(userId: Int, permission: String): Set<String> =
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
index cc3584b..dfd8f6b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
@@ -28,7 +28,7 @@
     }
 
     fun getEnterpriseString(updatableStringId: String, resId: Int): String =
-        resources.getString(updatableStringId) { context.getString(resId) }
+        checkNotNull(resources.getString(updatableStringId) { context.getString(resId) })
 
     fun getProfileTitle(isManagedProfile: Boolean): String = if (isManagedProfile) {
         getEnterpriseString(WORK_CATEGORY_HEADER, R.string.category_work)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index cee750e..ea83e1d 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -58,7 +58,7 @@
                 .semantics(mergeDescendants = true) {},
             horizontalAlignment = Alignment.CenterHorizontally,
         ) {
-            val app = packageInfo.applicationInfo
+            val app = checkNotNull(packageInfo.applicationInfo)
             Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
                 AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
             }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 7f82be4..62c5f70 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -144,7 +144,7 @@
         footerContent = { AnnotatedText(footerResId) },
         packageManagers = packageManagers,
     ) {
-        val model = createSwitchModel(applicationInfo)
+        val model = createSwitchModel(checkNotNull(applicationInfo))
         val restrictions = Restrictions(userId, switchRestrictionKeys)
         RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory)
     }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 80cf6c3..20740dc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -223,6 +223,7 @@
         VALIDATORS.put(System.NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.WEAR_TTS_PREWARM_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.UNREAD_NOTIFICATION_DOT_INDICATOR, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.AUTO_LAUNCH_MEDIA_CONTROLS, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c697c1f..203efbf 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -103,6 +103,7 @@
                     Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
                     Settings.System.SCREEN_BRIGHTNESS_FLOAT,
                     Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
+                    Settings.System.WEAR_TTS_PREWARM_ENABLED,
                     Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
                     Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
                     );
diff --git a/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt b/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
index 92d752c..4837aad 100644
--- a/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
+++ b/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
@@ -88,6 +88,7 @@
         } catch (e: Exception) {
             return Result.Failure(e)
         }
+        checkNotNull(signingInfo)
         return if (signingInfo.hasMultipleSigners()) {
             signingInfo.apkContentsSigners
         } else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index da48762..0a100ba 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,26 +14,20 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.keyguard.ui.composable
 
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
+import android.view.View
+import android.view.ViewGroup
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.SceneKey
@@ -42,6 +36,7 @@
 import com.android.systemui.scene.ui.composable.ComposableScene
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
@@ -54,6 +49,7 @@
 constructor(
     @Application private val applicationScope: CoroutineScope,
     private val viewModel: LockscreenSceneViewModel,
+    @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
 ) : ComposableScene {
     override val key = SceneKey.Lockscreen
 
@@ -72,6 +68,7 @@
     ) {
         LockscreenScene(
             viewModel = viewModel,
+            viewProvider = viewProvider,
             modifier = modifier,
         )
     }
@@ -89,25 +86,22 @@
 @Composable
 private fun LockscreenScene(
     viewModel: LockscreenSceneViewModel,
+    viewProvider: () -> View,
     modifier: Modifier = Modifier,
 ) {
-    // TODO(b/280879610): implement the real UI.
-
-    val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState()
-
-    Box(modifier = modifier) {
-        Column(
-            horizontalAlignment = Alignment.CenterHorizontally,
-            modifier = Modifier.align(Alignment.Center)
-        ) {
-            Text("Lockscreen", style = MaterialTheme.typography.headlineMedium)
-            Row(
-                horizontalArrangement = Arrangement.spacedBy(8.dp),
-            ) {
-                Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) }
-
-                Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") }
+    AndroidView(
+        factory = { _ ->
+            val keyguardRootView = viewProvider()
+            // Remove the KeyguardRootView from any parent it might already have in legacy code just
+            // in case (a view can't have two parents).
+            (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+            keyguardRootView
+        },
+        update = { keyguardRootView ->
+            keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener {
+                viewModel.onLockButtonClicked()
             }
-        }
-    }
+        },
+        modifier = modifier,
+    )
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index ab0225d..966e183 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -51,15 +51,18 @@
 private val KEY_TIMESTAMP = "appliedTimestamp"
 private val KNOWN_PLUGINS =
     mapOf<String, List<ClockMetadata>>(
-        "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")),
-        "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")),
-        "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")),
-        "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")),
-        "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")),
-        "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")),
-        "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")),
-        "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")),
-        "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")),
+        "com.android.systemui.clocks.bignum" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")),
+        "com.android.systemui.clocks.calligraphy" to
+            listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")),
+        "com.android.systemui.clocks.flex" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")),
+        "com.android.systemui.clocks.growth" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")),
+        "com.android.systemui.clocks.handwritten" to
+            listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")),
+        "com.android.systemui.clocks.inflate" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")),
+        "com.android.systemui.clocks.metro" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")),
+        "com.android.systemui.clocks.numoverlap" to
+            listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")),
+        "com.android.systemui.clocks.weather" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")),
     )
 
 private fun <TKey : Any, TVal : Any> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 92083b0..785177e 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -292,6 +292,7 @@
 -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
diff --git a/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
new file mode 100644
index 0000000..a8abd79
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" />
+    <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" />
+    <item android:color="@color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
index a8c0349..47a2965 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -32,6 +32,12 @@
                 <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
             </shape>
         </item>
+        <item>
+            <shape android:shape="rectangle">
+                <solid android:color="@color/qs_footer_power_button_overlay_color"/>
+                <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
+            </shape>
+        </item>
 
     </ripple>
 </inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml
new file mode 100644
index 0000000..24222f7
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_pin_content_view.xml
@@ -0,0 +1,101 @@
+<!--
+  ~ 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.
+  -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <RelativeLayout
+    android:id="@+id/auth_credential_header"
+    style="?headerStyle"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content"
+    android:layout_weight="1">
+
+    <ImageView
+        android:id="@+id/icon"
+        style="?headerIconStyle"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentTop="true"
+        android:contentDescription="@null"/>
+
+    <TextView
+        android:id="@+id/title"
+        style="?titleTextAppearance"
+        android:layout_below="@id/icon"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/subtitle"
+        style="?subTitleTextAppearance"
+        android:layout_below="@id/title"
+        android:layout_alignParentLeft="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/description"
+        style="?descriptionTextAppearance"
+        android:layout_below="@id/subtitle"
+        android:layout_alignParentLeft="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+  </RelativeLayout>
+
+  <FrameLayout
+      android:id="@+id/auth_credential_input"
+      android:layout_width="0dp"
+      android:layout_height="match_parent"
+      android:layout_weight="1"
+      android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:orientation="vertical">
+
+      <ImeAwareEditText
+          android:id="@+id/lockPassword"
+          style="?passwordTextAppearance"
+          android:layout_width="208dp"
+          android:layout_height="wrap_content"
+          android:layout_gravity="center_horizontal"
+          android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+          android:inputType="textPassword"
+          android:minHeight="48dp"/>
+
+      <TextView
+          android:id="@+id/error"
+          style="?errorTextAppearance"
+          android:layout_gravity="center_horizontal"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"/>
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/emergencyCallButton"
+        style="@style/AuthCredentialEmergencyButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:layout_gravity="center_horizontal|bottom"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="12dp"
+        android:text="@string/work_challenge_emergency_button_text"/>
+  </FrameLayout>
+
+</merge>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index e439f77..8ac7583 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -23,84 +23,6 @@
     android:elevation="@dimen/biometric_dialog_elevation"
     android:theme="?app:attr/lockPinPasswordStyle">
 
-    <RelativeLayout
-        android:id="@+id/auth_credential_header"
-        style="?headerStyle"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent">
-
-        <ImageView
-            android:id="@+id/icon"
-            style="?headerIconStyle"
-            android:layout_alignParentLeft="true"
-            android:layout_alignParentTop="true"
-            android:contentDescription="@null"/>
-
-        <TextView
-            android:id="@+id/title"
-            style="?titleTextAppearance"
-            android:layout_below="@id/icon"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-        <TextView
-            android:id="@+id/subtitle"
-            style="?subTitleTextAppearance"
-            android:layout_below="@id/title"
-            android:layout_alignParentLeft="true"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-        <TextView
-            android:id="@+id/description"
-            style="?descriptionTextAppearance"
-            android:layout_below="@id/subtitle"
-            android:layout_alignParentLeft="true"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-    </RelativeLayout>
-
-    <FrameLayout
-        android:id="@+id/auth_credential_input"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal|top"
-            android:orientation="vertical">
-
-            <ImeAwareEditText
-                android:id="@+id/lockPassword"
-                style="?passwordTextAppearance"
-                android:layout_width="208dp"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_horizontal"
-                android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
-                android:inputType="textPassword"
-                android:minHeight="48dp"/>
-
-            <TextView
-                android:id="@+id/error"
-                style="?errorTextAppearance"
-                android:layout_gravity="center_horizontal"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"/>
-        </LinearLayout>
-
-        <Button
-            android:id="@+id/emergencyCallButton"
-            style="@style/AuthCredentialEmergencyButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            android:layout_gravity="center_horizontal|bottom"
-            android:layout_marginTop="12dp"
-            android:layout_marginBottom="12dp"
-            android:text="@string/work_challenge_emergency_button_text"/>
-    </FrameLayout>
+    <include layout="@layout/auth_credential_password_pin_content_view" />
 
 </com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml
new file mode 100644
index 0000000..8ac7583
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_pin_view.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<com.android.systemui.biometrics.ui.CredentialPasswordView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:elevation="@dimen/biometric_dialog_elevation"
+    android:theme="?app:attr/lockPinPasswordStyle">
+
+    <include layout="@layout/auth_credential_password_pin_content_view" />
+
+</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml b/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml
new file mode 100644
index 0000000..11284fd
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_password_pin_content_view.xml
@@ -0,0 +1,104 @@
+<!--
+  ~ 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.
+  -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <ScrollView
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <RelativeLayout
+      android:id="@+id/auth_credential_header"
+      style="?headerStyle"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingBottom="0dp">
+
+      <ImageView
+          android:id="@+id/icon"
+          style="?headerIconStyle"
+          android:layout_alignParentLeft="true"
+          android:layout_alignParentTop="true"
+          android:contentDescription="@null" />
+
+      <TextView
+          android:id="@+id/title"
+          style="?titleTextAppearance"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:layout_below="@id/icon" />
+
+      <TextView
+          android:id="@+id/subtitle"
+          style="?subTitleTextAppearance"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:layout_below="@id/title" />
+
+      <TextView
+          android:id="@+id/description"
+          style="?descriptionTextAppearance"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:layout_below="@id/subtitle" />
+
+    </RelativeLayout>
+
+  </ScrollView>
+
+  <FrameLayout
+      android:id="@+id/auth_credential_input"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:orientation="vertical">
+
+      <ImeAwareEditText
+          android:id="@+id/lockPassword"
+          style="?passwordTextAppearance"
+          android:layout_width="208dp"
+          android:layout_height="wrap_content"
+          android:layout_gravity="center_horizontal"
+          android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+          android:inputType="textPassword"
+          android:minHeight="48dp"/>
+
+      <TextView
+          android:id="@+id/error"
+          style="?errorTextAppearance"
+          android:layout_gravity="center_horizontal"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"/>
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/emergencyCallButton"
+        style="@style/AuthCredentialEmergencyButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:layout_gravity="center_horizontal|bottom"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="12dp"
+        android:text="@string/work_challenge_emergency_button_text"/>
+  </FrameLayout>
+
+</merge>
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 9336845..f8d9a87 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -23,88 +23,6 @@
     android:orientation="vertical"
     android:theme="?app:attr/lockPinPasswordStyle">
 
-    <ScrollView
-        android:id="@+id/auth_credential_header"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <RelativeLayout
-            style="?headerStyle"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <ImageView
-                android:id="@+id/icon"
-                style="?headerIconStyle"
-                android:layout_alignParentLeft="true"
-                android:layout_alignParentTop="true"
-                android:contentDescription="@null" />
-
-            <TextView
-                android:id="@+id/title"
-                style="?titleTextAppearance"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/icon" />
-
-            <TextView
-                android:id="@+id/subtitle"
-                style="?subTitleTextAppearance"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/title" />
-
-            <TextView
-                android:id="@+id/description"
-                style="?descriptionTextAppearance"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/subtitle" />
-
-        </RelativeLayout>
-
-    </ScrollView>
-
-    <FrameLayout
-        android:id="@+id/auth_credential_input"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal|top"
-            android:orientation="vertical">
-
-            <ImeAwareEditText
-                android:id="@+id/lockPassword"
-                style="?passwordTextAppearance"
-                android:layout_width="208dp"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_horizontal"
-                android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
-                android:inputType="textPassword"
-                android:minHeight="48dp"/>
-
-            <TextView
-                android:id="@+id/error"
-                style="?errorTextAppearance"
-                android:layout_gravity="center_horizontal"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"/>
-        </LinearLayout>
-
-        <Button
-            android:id="@+id/emergencyCallButton"
-            style="@style/AuthCredentialEmergencyButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            android:layout_gravity="center_horizontal|bottom"
-            android:layout_marginTop="12dp"
-            android:layout_marginBottom="12dp"
-            android:text="@string/work_challenge_emergency_button_text"/>
-    </FrameLayout>
+    <include layout="@layout/auth_credential_password_pin_content_view" />
 
 </com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pin_view.xml b/packages/SystemUI/res/layout/auth_credential_pin_view.xml
new file mode 100644
index 0000000..a1cf807
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_pin_view.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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.
+  -->
+
+<com.android.systemui.biometrics.ui.CredentialPasswordView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:elevation="@dimen/biometric_dialog_elevation"
+    android:orientation="vertical"
+    android:theme="?app:attr/lockPinPasswordStyle">
+
+  <include layout="@layout/auth_credential_password_pin_content_view" />
+
+</com.android.systemui.biometrics.ui.CredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
index 66c2155..1e5b249 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog_spinner_item_text.xml
@@ -13,15 +13,32 @@
   See the License for the specific language governing permissions and
   limitations under the License.
   -->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@android:id/text1"
-    android:textAppearance="?android:attr/textAppearanceMedium"
-    android:textColor="?androidprv:attr/textColorOnAccent"
-    android:singleLine="true"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/screenrecord_spinner_height"
-    android:gravity="center_vertical"
-    android:ellipsize="marquee"
+    android:layout_height="wrap_content"
+    android:minHeight="@dimen/screenrecord_spinner_height"
+    android:paddingEnd="@dimen/screenrecord_spinner_text_padding_end"
     android:paddingStart="@dimen/screenrecord_spinner_text_padding_start"
-    android:paddingEnd="@dimen/screenrecord_spinner_text_padding_end"/>
\ No newline at end of file
+    android:gravity="center_vertical"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:ellipsize="marquee"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?androidprv:attr/textColorOnAccent" />
+
+    <TextView
+        android:id="@android:id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:ellipsize="marquee"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?androidprv:attr/colorError" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index fd1de25..04eae64 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -223,6 +223,9 @@
     <!-- Communal mode -->
     <item type="id" name="communal_widget_wrapper" />
 
+    <!-- Values assigned to the views in Biometrics Prompt -->
+    <item type="id" name="pin_pad"/>
+
     <!--
     Used to tag views programmatically added to the smartspace area so they can be more easily
     removed later.
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 77ffe19..b37aeee 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1107,6 +1107,8 @@
     <string name="media_projection_entry_app_permission_dialog_warning_single_app">When you’re sharing, recording, or casting an app, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
     <!-- 1P/3P apps media projection permission button to continue with app selection or recording [CHAR LIMIT=60] -->
     <string name="media_projection_entry_app_permission_dialog_continue">Start</string>
+    <!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
+    <string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
 
     <!-- Casting that launched by SysUI (i.e. when there is no app name) -->
     <!-- System casting media projection permission dialog title. [CHAR LIMIT=100] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 4bc9491..33e453c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -76,26 +76,11 @@
     void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
 
     /**
-     * Sent when screen turned on and ready to use (blocker scrim is hidden)
-     */
-    void onScreenTurnedOn() = 21;
-
-    /**
      * Sent when the desired dark intensity of the nav buttons has changed
      */
     void onNavButtonsDarkIntensityChanged(float darkIntensity) = 22;
 
      /**
-      * Sent when screen started turning on.
-      */
-     void onScreenTurningOn() = 23;
-
-     /**
-      * Sent when screen started turning off.
-      */
-     void onScreenTurningOff() = 24;
-
-     /**
       * Sent when split keyboard shortcut is triggered to enter stage split.
       */
      void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index cab54d0..8200e5c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -39,6 +39,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
@@ -76,6 +77,8 @@
     private static final String TAG = "RotationButtonController";
     private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
     private static final int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000;
+    private static final boolean OEM_DISALLOW_ROTATION_IN_SUW =
+            SystemProperties.getBoolean("ro.setupwizard.rotation_locked", false);
     private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
 
     private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
@@ -375,6 +378,12 @@
     }
 
     public void onRotationProposal(int rotation, boolean isValid) {
+        boolean isUserSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+        if (!isUserSetupComplete && OEM_DISALLOW_ROTATION_IN_SUW) {
+            return;
+        }
+
         int windowRotation = mWindowRotationProvider.get();
 
         if (!mRotationButton.acceptRotationProposal()) {
@@ -497,8 +506,7 @@
     boolean canShowRotationButton() {
         return mIsNavigationBarShowing
             || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT
-            || isGesturalMode(mNavBarMode)
-            || mTaskBarVisible;
+            || isGesturalMode(mNavBarMode);
     }
 
     @DrawableRes
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index 899cad89..006974c 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -66,7 +66,7 @@
         window.isNavigationBarContrastEnforced = false
         window.navigationBarColor = Color.TRANSPARENT
 
-        clock = findViewById(R.id.clock)
+        clock = requireViewById(R.id.clock)
         keyguardStatusViewController =
             keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply {
                 setDisplayedOnSecondaryDisplay()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 802a550..7464c88 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -499,6 +499,8 @@
                         R.layout.auth_credential_pattern_view, null, false);
                 break;
             case Utils.CREDENTIAL_PIN:
+                mCredentialView = factory.inflate(R.layout.auth_credential_pin_view, null, false);
+                break;
             case Utils.CREDENTIAL_PASSWORD:
                 mCredentialView = factory.inflate(
                         R.layout.auth_credential_password_view, null, false);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
index 20c3e40..f7f9103 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/UdfpsModule.kt
@@ -56,10 +56,10 @@
                         )
                     )
                 } else {
-                    BoundingBoxOverlapDetector()
+                    BoundingBoxOverlapDetector(values[2])
                 }
             } else {
-                return BoundingBoxOverlapDetector()
+                return BoundingBoxOverlapDetector(1f)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
index d2cb849..5b0bd95 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FacePropertyRepository.kt
@@ -20,7 +20,10 @@
 import android.hardware.face.FaceManager
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
+import android.util.Log
+import com.android.systemui.biometrics.shared.model.LockoutMode
 import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.biometrics.shared.model.toLockoutMode
 import com.android.systemui.biometrics.shared.model.toSensorStrength
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow
@@ -29,16 +32,18 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
 
 /** A repository for the global state of Face sensor. */
 interface FacePropertyRepository {
     /** Face sensor information, null if it is not available. */
-    val sensorInfo: Flow<FaceSensorInfo?>
+    val sensorInfo: StateFlow<FaceSensorInfo?>
+
+    /** Get the current lockout mode for the user. This makes a binder based service call. */
+    suspend fun getLockoutMode(userId: Int): LockoutMode
 }
 
 /** Describes a biometric sensor */
@@ -49,33 +54,39 @@
 @SysUISingleton
 class FacePropertyRepositoryImpl
 @Inject
-constructor(@Application private val applicationScope: CoroutineScope, faceManager: FaceManager?) :
-    FacePropertyRepository {
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val faceManager: FaceManager?
+) : FacePropertyRepository {
 
-    private val sensorProps: Flow<List<FaceSensorPropertiesInternal>> =
-        faceManager?.let {
-            ConflatedCallbackFlow.conflatedCallbackFlow {
-                    val callback =
-                        object : IFaceAuthenticatorsRegisteredCallback.Stub() {
-                            override fun onAllAuthenticatorsRegistered(
-                                sensors: List<FaceSensorPropertiesInternal>
-                            ) {
-                                trySendWithFailureLogging(
-                                    sensors,
-                                    TAG,
-                                    "onAllAuthenticatorsRegistered"
-                                )
-                            }
+    override val sensorInfo: StateFlow<FaceSensorInfo?> =
+        ConflatedCallbackFlow.conflatedCallbackFlow {
+                val callback =
+                    object : IFaceAuthenticatorsRegisteredCallback.Stub() {
+                        override fun onAllAuthenticatorsRegistered(
+                            sensors: List<FaceSensorPropertiesInternal>,
+                        ) {
+                            if (sensors.isEmpty()) return
+                            trySendWithFailureLogging(
+                                FaceSensorInfo(
+                                    sensors.first().sensorId,
+                                    sensors.first().sensorStrength.toSensorStrength()
+                                ),
+                                TAG,
+                                "onAllAuthenticatorsRegistered"
+                            )
                         }
-                    it.addAuthenticatorsRegisteredCallback(callback)
-                    awaitClose {}
-                }
-                .shareIn(applicationScope, SharingStarted.Eagerly)
-        }
-            ?: flowOf(emptyList())
+                    }
+                faceManager?.addAuthenticatorsRegisteredCallback(callback)
+                awaitClose {}
+            }
+            .onEach { Log.d(TAG, "sensorProps changed: $it") }
+            .stateIn(applicationScope, SharingStarted.Eagerly, null)
 
-    override val sensorInfo: Flow<FaceSensorInfo?> =
-        sensorProps
-            .map { it.firstOrNull() }
-            .map { it?.let { FaceSensorInfo(it.sensorId, it.sensorStrength.toSensorStrength()) } }
+    override suspend fun getLockoutMode(userId: Int): LockoutMode {
+        if (sensorInfo.value == null || faceManager == null) {
+            return LockoutMode.NONE
+        }
+        return faceManager.getLockoutModeForUser(sensorInfo.value!!.id, userId).toLockoutMode()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt
new file mode 100644
index 0000000..68bba32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/LockoutMode.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.biometrics.shared.model
+
+import android.hardware.biometrics.BiometricConstants
+
+/** Lockout mode. Represents [BiometricConstants.LockoutMode]. */
+enum class LockoutMode {
+    NONE,
+    TIMED,
+    PERMANENT,
+}
+
+/** Convert [this] to corresponding [LockoutMode] */
+fun Int.toLockoutMode(): LockoutMode =
+    when (this) {
+        BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT -> LockoutMode.PERMANENT
+        BiometricConstants.BIOMETRIC_LOCKOUT_TIMED -> LockoutMode.TIMED
+        else -> LockoutMode.NONE
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
index cf6044f..9b946db 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetector.kt
@@ -17,16 +17,30 @@
 package com.android.systemui.biometrics.udfps
 
 import android.graphics.Rect
+import android.os.Build
+import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
 
 /** Returns whether the touch coordinates are within the sensor's bounding box. */
 @SysUISingleton
-class BoundingBoxOverlapDetector : OverlapDetector {
+class BoundingBoxOverlapDetector(private val targetSize: Float) : OverlapDetector {
+
+    private val TAG = "BoundingBoxOverlapDetector"
+
     override fun isGoodOverlap(
         touchData: NormalizedTouchData,
         nativeSensorBounds: Rect,
         nativeOverlayBounds: Rect,
-    ): Boolean =
-        touchData.isWithinBounds(nativeOverlayBounds) &&
-            touchData.isWithinBounds(nativeSensorBounds)
+    ): Boolean {
+        val scaledRadius = (nativeSensorBounds.width() / 2) * targetSize
+        val scaledSensorBounds =
+            Rect(
+                (nativeSensorBounds.centerX() - scaledRadius).toInt(),
+                (nativeSensorBounds.centerY() - scaledRadius).toInt(),
+                (nativeSensorBounds.centerX() + scaledRadius).toInt(),
+                (nativeSensorBounds.centerY() + scaledRadius).toInt(),
+            )
+
+        return touchData.isWithinBounds(scaledSensorBounds)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index 709fe85..6c5cc48 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -8,11 +8,8 @@
 import android.view.WindowInsets
 import android.view.WindowInsets.Type.ime
 import android.view.accessibility.AccessibilityManager
-import android.widget.ImageView
-import android.widget.ImeAwareEditText
 import android.widget.LinearLayout
 import android.widget.TextView
-import androidx.core.view.isGone
 import com.android.systemui.R
 import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.ui.binder.CredentialViewBinder
@@ -22,14 +19,6 @@
 class CredentialPasswordView(context: Context, attrs: AttributeSet?) :
     LinearLayout(context, attrs), CredentialView, View.OnApplyWindowInsetsListener {
 
-    private lateinit var titleView: TextView
-    private lateinit var subtitleView: TextView
-    private lateinit var descriptionView: TextView
-    private lateinit var iconView: ImageView
-    private lateinit var passwordField: ImeAwareEditText
-    private lateinit var credentialHeader: View
-    private lateinit var credentialInput: View
-
     private var bottomInset: Int = 0
 
     private val accessibilityManager by lazy {
@@ -48,90 +37,32 @@
 
     override fun onFinishInflate() {
         super.onFinishInflate()
-
-        titleView = requireViewById(R.id.title)
-        subtitleView = requireViewById(R.id.subtitle)
-        descriptionView = requireViewById(R.id.description)
-        iconView = requireViewById(R.id.icon)
-        passwordField = requireViewById(R.id.lockPassword)
-        credentialHeader = requireViewById(R.id.auth_credential_header)
-        credentialInput = requireViewById(R.id.auth_credential_input)
-
         setOnApplyWindowInsetsListener(this)
     }
 
-    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
-        super.onLayout(changed, left, top, right, bottom)
-
-        val inputLeftBound: Int
-        var inputTopBound: Int
-        var headerRightBound = right
-        var headerTopBounds = top
-        var headerBottomBounds = bottom
-        val subTitleBottom: Int = if (subtitleView.isGone) titleView.bottom else subtitleView.bottom
-        val descBottom = if (descriptionView.isGone) subTitleBottom else descriptionView.bottom
-        if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
-            inputTopBound = (bottom - credentialInput.height) / 2
-            inputLeftBound = (right - left) / 2
-            headerRightBound = inputLeftBound
-            if (descriptionView.bottom > headerBottomBounds) {
-                headerTopBounds -= iconView.bottom.coerceAtMost(bottomInset)
-                credentialHeader.layout(left, headerTopBounds, headerRightBound, bottom)
-            }
-        } else {
-            inputTopBound = descBottom + (bottom - descBottom - credentialInput.height) / 2
-            inputLeftBound = (right - left - credentialInput.width) / 2
-
-            if (bottom - inputTopBound < credentialInput.height) {
-                inputTopBound = bottom - credentialInput.height
-            }
-
-            if (descriptionView.bottom > inputTopBound) {
-                credentialHeader.layout(left, headerTopBounds, headerRightBound, inputTopBound)
-            }
-        }
-
-        credentialInput.layout(inputLeftBound, inputTopBound, right, bottom)
-    }
-
-    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-
-        val newWidth = MeasureSpec.getSize(widthMeasureSpec)
-        val newHeight = MeasureSpec.getSize(heightMeasureSpec) - bottomInset
-
-        setMeasuredDimension(newWidth, newHeight)
-
-        val halfWidthSpec = MeasureSpec.makeMeasureSpec(width / 2, MeasureSpec.AT_MOST)
-        val fullHeightSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.UNSPECIFIED)
-        if (resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
-            measureChildren(halfWidthSpec, fullHeightSpec)
-        } else {
-            measureChildren(widthMeasureSpec, fullHeightSpec)
-        }
-    }
-
     override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
-        val bottomInsets = insets.getInsets(ime())
-        if (bottomInset != bottomInsets.bottom) {
-            bottomInset = bottomInsets.bottom
-
-            if (bottomInset > 0 && resources.configuration.orientation == ORIENTATION_LANDSCAPE) {
-                titleView.isSingleLine = true
-                titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
-                titleView.marqueeRepeatLimit = -1
-                // select to enable marquee unless a screen reader is enabled
-                titleView.isSelected = accessibilityManager?.shouldMarquee() ?: false
-            } else {
-                titleView.isSingleLine = false
-                titleView.ellipsize = null
-                // select to enable marquee unless a screen reader is enabled
-                titleView.isSelected = false
+        val imeBottomInset = insets.getInsets(ime()).bottom
+        if (bottomInset != imeBottomInset) {
+            val titleView: TextView? = findViewById(R.id.title)
+            if (titleView != null) {
+                if (
+                    bottomInset > 0 && resources.configuration.orientation == ORIENTATION_LANDSCAPE
+                ) {
+                    titleView.isSingleLine = true
+                    titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
+                    titleView.marqueeRepeatLimit = -1
+                    // select to enable marquee unless a screen reader is enabled
+                    titleView.isSelected = accessibilityManager.shouldMarquee()
+                } else {
+                    titleView.isSingleLine = false
+                    titleView.ellipsize = null
+                    // select to enable marquee unless a screen reader is enabled
+                    titleView.isSelected = false
+                }
             }
-
-            requestLayout()
         }
-        return insets
+        setPadding(paddingLeft, paddingTop, paddingRight, imeBottomInset)
+        return insets.inset(0, 0, 0, imeBottomInset)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt
new file mode 100644
index 0000000..cf6865c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/IPinPad.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 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.biometrics.ui
+
+/**
+ * Interface for PinPad in auth_credential_pin_view. This is needed when a custom pin pad is
+ * preferred to the IME To use a PinPad, one needs to implement IPinPad interface and provide it in
+ * auth_credential_pin_view and specify the id as [pin_pad]
+ */
+interface IPinPad {
+    fun setPinPadClickListener(pinPadClickListener: PinPadClickListener)
+}
+
+/** The call back interface for onClick event in the view. */
+interface PinPadClickListener {
+    /**
+     * One of the digit key has been clicked.
+     *
+     * @param digit A String representing a digit between 0 and 9.
+     */
+    fun onDigitKeyClick(digit: String?)
+
+    /** The backspace key has been clicked. */
+    fun onBackspaceClick()
+
+    /** The enter key has been clicked. */
+    fun onEnterKeyClick()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
index c27d7152..996b62e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
@@ -15,6 +15,7 @@
 import com.android.systemui.R
 import com.android.systemui.biometrics.ui.CredentialPasswordView
 import com.android.systemui.biometrics.ui.CredentialView
+import com.android.systemui.biometrics.ui.IPinPad
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import kotlinx.coroutines.awaitCancellation
@@ -53,13 +54,19 @@
                 }
             )
             passwordField.setOnKeyListener(OnBackButtonListener(onBackInvokedCallback))
-
+            val pinPadView = view.findViewById(R.id.pin_pad) as? IPinPad
+            if (pinPadView != null) {
+                PinPadViewBinder.bind(pinPadView, view)
+            }
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 // dismiss on a valid credential check
                 launch {
                     viewModel.validatedAttestation.collect { attestation ->
                         if (attestation != null) {
-                            imeManager.hideSoftInputFromWindow(view.windowToken, 0 /* flags */)
+                            imeManager.hideSoftInputFromWindow(
+                                view.windowToken,
+                                0 // flag
+                            )
                             host.onCredentialMatched(attestation)
                         } else {
                             passwordField.setText("")
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 4ac9f96..25fe619 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -47,6 +47,7 @@
         val descriptionView: TextView = view.requireViewById(R.id.description)
         val iconView: ImageView? = view.findViewById(R.id.icon)
         val errorView: TextView = view.requireViewById(R.id.error)
+        val cancelButton: Button? = view.findViewById(R.id.cancel_button)
         val emergencyButtonView: Button = view.requireViewById(R.id.emergencyCallButton)
 
         var errorTimer: Job? = null
@@ -60,7 +61,7 @@
                     updateForContentDimensions(
                         containerWidth,
                         containerHeight,
-                        0 /* animateDurationMs */
+                        0 // animateDurationMs
                     )
                 }
             }
@@ -103,7 +104,18 @@
                                 }
                             }
                         }
-                        .collect { errorView.textOrHide = it }
+                        .collect { it ->
+                            val hasError = !it.isNullOrBlank()
+                            errorView.visibility =
+                                if (hasError) {
+                                    View.VISIBLE
+                                } else if (cancelButton != null) {
+                                    View.INVISIBLE
+                                } else {
+                                    View.GONE
+                                }
+                            errorView.text = if (hasError) it else ""
+                        }
                 }
 
                 // show an extra dialog if the remaining attempts becomes low
@@ -117,6 +129,8 @@
             }
         }
 
+        cancelButton?.setOnClickListener { host.onCredentialAborted() }
+
         // bind the auth widget
         when (view) {
             is CredentialPasswordView ->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
new file mode 100644
index 0000000..906206c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PinPadViewBinder.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 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.biometrics.ui.binder
+
+import android.view.KeyEvent
+import android.widget.ImeAwareEditText
+import com.android.internal.widget.LockscreenCredential
+import com.android.systemui.R
+import com.android.systemui.biometrics.ui.CredentialPasswordView
+import com.android.systemui.biometrics.ui.IPinPad
+import com.android.systemui.biometrics.ui.PinPadClickListener
+
+/** Binder for IPinPad */
+object PinPadViewBinder {
+    /** Implements a PinPadClickListener inside a pin pad */
+    @JvmStatic
+    fun bind(view: IPinPad, credentialPasswordView: CredentialPasswordView) {
+        val passwordField: ImeAwareEditText =
+            credentialPasswordView.requireViewById(R.id.lockPassword)
+        view.setPinPadClickListener(
+            object : PinPadClickListener {
+
+                override fun onDigitKeyClick(digit: String?) {
+                    passwordField.append(digit)
+                }
+
+                override fun onBackspaceClick() {
+                    val pin = LockscreenCredential.createPinOrNone(passwordField.text)
+                    if (pin.size() > 0) {
+                        passwordField.text.delete(
+                            passwordField.selectionEnd - 1,
+                            passwordField.selectionEnd
+                        )
+                    }
+                    pin.zeroize()
+                }
+
+                override fun onEnterKeyClick() {
+                    passwordField.dispatchKeyEvent(
+                        KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0)
+                    )
+                    passwordField.dispatchKeyEvent(
+                        KeyEvent(0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0)
+                    )
+                }
+            }
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 4dc7720..e8b8f54 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -289,7 +289,7 @@
             if (authenticateAfterError) {
                 showAuthenticating(messageAfterError)
             } else {
-                showHelp(messageAfterError)
+                showInfo(messageAfterError)
             }
         }
     }
@@ -309,12 +309,15 @@
     private fun supportsRetry(failedModality: BiometricModality) =
         failedModality == BiometricModality.Face
 
+    suspend fun showHelp(message: String) = showHelp(message, clearIconError = false)
+    suspend fun showInfo(message: String) = showHelp(message, clearIconError = true)
+
     /**
      * Show a persistent help message.
      *
      * Will be show even if the user has already authenticated.
      */
-    suspend fun showHelp(message: String) {
+    private suspend fun showHelp(message: String, clearIconError: Boolean) {
         val alreadyAuthenticated = _isAuthenticated.value.isAuthenticated
         if (!alreadyAuthenticated) {
             _isAuthenticating.value = false
@@ -329,6 +332,8 @@
                 AuthBiometricView.STATE_PENDING_CONFIRMATION
             } else if (alreadyAuthenticated && !isConfirmationRequired.first()) {
                 AuthBiometricView.STATE_AUTHENTICATED
+            } else if (clearIconError) {
+                AuthBiometricView.STATE_IDLE
             } else {
                 AuthBiometricView.STATE_HELP
             }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index 918e168..f2b4e09 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -58,7 +58,7 @@
      * ```
      */
     val panelExpansionAmount: StateFlow<Float>
-    val keyguardPosition: StateFlow<Float>
+    val keyguardPosition: StateFlow<Float?>
     val isBackButtonEnabled: StateFlow<Boolean?>
     /** Determines if user is already unlocked */
     val keyguardAuthenticated: StateFlow<Boolean?>
@@ -130,7 +130,7 @@
      */
     private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
     override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
-    private val _keyguardPosition = MutableStateFlow(0f)
+    private val _keyguardPosition = MutableStateFlow<Float?>(null)
     override val keyguardPosition = _keyguardPosition.asStateFlow()
     private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
     override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
@@ -244,6 +244,7 @@
             .logDiffsForTable(buffer, "", "PanelExpansionAmountMillis", -1)
             .launchIn(applicationScope)
         keyguardPosition
+            .filterNotNull()
             .map { it.toInt() }
             .logDiffsForTable(buffer, "", "KeyguardPosition", -1)
             .launchIn(applicationScope)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index c486603..0e0f1f6 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -94,7 +94,7 @@
     val startingDisappearAnimation: Flow<Runnable> =
         repository.primaryBouncerStartingDisappearAnimation.filterNotNull()
     val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
-    val keyguardPosition: Flow<Float> = repository.keyguardPosition
+    val keyguardPosition: Flow<Float> = repository.keyguardPosition.filterNotNull()
     val panelExpansionAmount: Flow<Float> = repository.panelExpansionAmount
     /** 0f = bouncer fully hidden. 1f = bouncer fully visible. */
     val bouncerExpansion: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 484be9c..1b2a9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@
 import com.android.systemui.settings.dagger.MultiUserUtilsModule
 import com.android.systemui.shortcut.ShortcutKeyDispatcher
 import com.android.systemui.statusbar.ImmersiveModeConfirmation
+import com.android.systemui.statusbar.gesture.GesturePointerEventListener
 import com.android.systemui.statusbar.notification.InstantAppNotifier
 import com.android.systemui.statusbar.phone.KeyguardLiftController
 import com.android.systemui.statusbar.phone.LockscreenWallpaper
@@ -182,6 +183,12 @@
     @ClassKey(ScreenDecorations::class)
     abstract fun bindScreenDecorations(sysui: ScreenDecorations): CoreStartable
 
+    /** Inject into GesturePointerEventHandler. */
+    @Binds
+    @IntoMap
+    @ClassKey(GesturePointerEventListener::class)
+    abstract fun bindGesturePointerEventListener(sysui: GesturePointerEventListener): CoreStartable
+
     /** Inject into SessionTracker.  */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 93eb103..8064cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -52,6 +52,7 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.UserRepository
 import java.io.PrintWriter
 import java.util.Arrays
@@ -71,6 +72,7 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
@@ -200,7 +202,7 @@
     private val keyguardSessionId: InstanceId?
         get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
 
-    private val _canRunFaceAuth = MutableStateFlow(true)
+    private val _canRunFaceAuth = MutableStateFlow(false)
     override val canRunFaceAuth: StateFlow<Boolean>
         get() = _canRunFaceAuth
 
@@ -281,7 +283,9 @@
                 } else {
                     keyguardRepository.isKeyguardGoingAway
                 },
-                userRepository.userSwitchingInProgress,
+                userRepository.selectedUser.map {
+                    it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
+                },
             )
             .onEach { anyOfThemIsTrue ->
                 if (anyOfThemIsTrue) {
@@ -325,6 +329,7 @@
                     cancelDetection()
                 }
             }
+            .flowOn(mainDispatcher)
             .logDiffsForTable(faceDetectLog, "", "canFaceDetectRun", false)
             .launchIn(applicationScope)
     }
@@ -410,6 +415,7 @@
                     cancel()
                 }
             }
+            .flowOn(mainDispatcher)
             .logDiffsForTable(faceAuthLog, "", "canFaceAuthRun", false)
             .launchIn(applicationScope)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index e501ece..635961b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -54,11 +54,7 @@
         if (event.handleAction()) {
             when (event.keyCode) {
                 KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
-                KeyEvent.KEYCODE_SPACE,
-                KeyEvent.KEYCODE_ENTER ->
-                    if (isDeviceInteractive()) {
-                        return collapseShadeLockedOrShowPrimaryBouncer()
-                    }
+                KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
             }
         }
         return false
@@ -94,22 +90,16 @@
                 (statusBarStateController.state != StatusBarState.SHADE) &&
                 statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
         if (shouldUnlockOnMenuPressed) {
-            return collapseShadeLockedOrShowPrimaryBouncer()
+            shadeController.animateCollapseShadeForced()
+            return true
         }
         return false
     }
 
-    private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
-        when (statusBarStateController.state) {
-            StatusBarState.SHADE -> return false
-            StatusBarState.SHADE_LOCKED -> {
-                shadeController.animateCollapseShadeForced()
-                return true
-            }
-            StatusBarState.KEYGUARD -> {
-                statusBarKeyguardViewManager.showPrimaryBouncer(true)
-                return true
-            }
+    private fun dispatchSpaceEvent(): Boolean {
+        if (isDeviceInteractive() && statusBarStateController.state != StatusBarState.SHADE) {
+            shadeController.animateCollapseShadeForced()
+            return true
         }
         return false
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index 2a3f852..4b8171f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -22,6 +22,8 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.CoreStartable
 import com.android.systemui.R
+import com.android.systemui.biometrics.data.repository.FacePropertyRepository
+import com.android.systemui.biometrics.shared.model.LockoutMode
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -35,6 +37,7 @@
 import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
@@ -51,6 +54,7 @@
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
+import kotlinx.coroutines.yield
 
 /**
  * Encapsulates business logic related face authentication being triggered for device entry from
@@ -72,6 +76,7 @@
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
     private val userRepository: UserRepository,
+    private val facePropertyRepository: FacePropertyRepository,
 ) : CoreStartable, KeyguardFaceAuthInteractor {
 
     private val listeners: MutableList<FaceAuthenticationListener> = mutableListOf()
@@ -92,7 +97,7 @@
                 faceAuthenticationLogger.bouncerVisibilityChanged()
                 runFaceAuth(
                     FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN,
-                    fallbackToDetect = true
+                    fallbackToDetect = false
                 )
             }
             .launchIn(applicationScope)
@@ -134,16 +139,25 @@
 
         // User switching should stop face auth and then when it is complete we should trigger face
         // auth so that the switched user can unlock the device with face auth.
-        userRepository.userSwitchingInProgress
-            .pairwise(false)
-            .onEach { (wasSwitching, isSwitching) ->
+        userRepository.selectedUser
+            .pairwise()
+            .onEach { (previous, curr) ->
+                val wasSwitching = previous.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
+                val isSwitching = curr.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
                 if (!wasSwitching && isSwitching) {
                     repository.pauseFaceAuth()
                 } else if (wasSwitching && !isSwitching) {
+                    val lockoutMode = facePropertyRepository.getLockoutMode(curr.userInfo.id)
+                    if (lockoutMode == LockoutMode.PERMANENT || lockoutMode == LockoutMode.TIMED) {
+                        repository.lockoutFaceAuth()
+                    }
                     repository.resumeFaceAuth()
+                    yield()
                     runFaceAuth(
                         FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
-                        fallbackToDetect = true
+                        // Fallback to detection if bouncer is not showing so that we can detect a
+                        // face and then show the bouncer to the user if face auth can't run
+                        fallbackToDetect = !primaryBouncerInteractor.isBouncerShowing()
                     )
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
new file mode 100644
index 0000000..c2d2725
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/qualifiers/KeyguardRootView.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2023 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.keyguard.qualifiers
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyguardRootView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
index a5b00e0..c8dab32 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
@@ -87,7 +87,7 @@
             }
             addListener(
                 object : AnimatorListenerAdapter() {
-                    override fun onAnimationEnd(animation: Animator?) {
+                    override fun onAnimationEnd(animation: Animator) {
                         updateIsAnimatingSurface()
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
new file mode 100644
index 0000000..c88737e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/LockscreenSceneModule.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.view
+
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.KeyguardViewConfigurator
+import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import dagger.Module
+import dagger.Provides
+import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@Module
+object LockscreenSceneModule {
+
+    @Provides
+    @SysUISingleton
+    @KeyguardRootView
+    fun viewProvider(
+        configurator: Provider<KeyguardViewConfigurator>,
+    ): () -> View {
+        return { configurator.get().getKeyguardRootView() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 6d3b7f1..93c4902 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,41 +16,22 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.R
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.scene.shared.model.SceneKey
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
 
 /** Models UI state and handles user input for the lockscreen scene. */
 @SysUISingleton
 class LockscreenSceneViewModel
 @Inject
 constructor(
-    @Application applicationScope: CoroutineScope,
     authenticationInteractor: AuthenticationInteractor,
     private val bouncerInteractor: BouncerInteractor,
 ) {
-    /** The icon for the "lock" button on the lockscreen. */
-    val lockButtonIcon: StateFlow<Icon> =
-        authenticationInteractor.isUnlocked
-            .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value),
-            )
-
     /** The key of the scene we should switch to when swiping up. */
     val upDestinationSceneKey: Flow<SceneKey> =
         authenticationInteractor.isUnlocked.map { isUnlocked ->
@@ -65,31 +46,4 @@
     fun onLockButtonClicked() {
         bouncerInteractor.showOrUnlockDevice()
     }
-
-    /** Notifies that some content on the lock screen was clicked. */
-    fun onContentClicked() {
-        bouncerInteractor.showOrUnlockDevice()
-    }
-
-    private fun upDestinationSceneKey(
-        canSwipeToDismiss: Boolean,
-    ): SceneKey {
-        return if (canSwipeToDismiss) SceneKey.Gone else SceneKey.Bouncer
-    }
-
-    private fun lockIcon(
-        isUnlocked: Boolean,
-    ): Icon {
-        return if (isUnlocked) {
-            Icon.Resource(
-                R.drawable.ic_device_lock_off,
-                ContentDescription.Resource(R.string.accessibility_unlock_button)
-            )
-        } else {
-            Icon.Resource(
-                R.drawable.ic_device_lock_on,
-                ContentDescription.Resource(R.string.accessibility_lock_icon)
-            )
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 72352e3..a9d2b30 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -36,6 +36,7 @@
 import android.content.pm.PackageManager;
 import android.graphics.Typeface;
 import android.media.projection.IMediaProjection;
+import android.media.projection.MediaProjectionConfig;
 import android.media.projection.MediaProjectionManager;
 import android.media.projection.ReviewGrantedConsentResult;
 import android.os.Bundle;
@@ -54,6 +55,7 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
 import com.android.systemui.screenrecord.ScreenShareOption;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -71,6 +73,7 @@
 
     private final FeatureFlags mFeatureFlags;
     private final Lazy<ScreenCaptureDevicePolicyResolver> mScreenCaptureDevicePolicyResolver;
+    private final ActivityStarter mActivityStarter;
 
     private String mPackageName;
     private int mUid;
@@ -86,8 +89,10 @@
 
     @Inject
     public MediaProjectionPermissionActivity(FeatureFlags featureFlags,
-            Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver) {
+            Lazy<ScreenCaptureDevicePolicyResolver> screenCaptureDevicePolicyResolver,
+            ActivityStarter activityStarter) {
         mFeatureFlags = featureFlags;
+        mActivityStarter = activityStarter;
         mScreenCaptureDevicePolicyResolver = screenCaptureDevicePolicyResolver;
     }
 
@@ -208,11 +213,13 @@
         // the correct screen width when in split screen.
         Context dialogContext = getApplicationContext();
         if (isPartialScreenSharingEnabled()) {
-            mDialog = new MediaProjectionPermissionDialog(dialogContext, () -> {
-                ScreenShareOption selectedOption =
-                        ((MediaProjectionPermissionDialog) mDialog).getSelectedScreenShareOption();
-                grantMediaProjectionPermission(selectedOption.getMode());
-            }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
+            mDialog = new MediaProjectionPermissionDialog(dialogContext, getMediaProjectionConfig(),
+                    () -> {
+                        MediaProjectionPermissionDialog dialog =
+                                (MediaProjectionPermissionDialog) mDialog;
+                        ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
+                        grantMediaProjectionPermission(selectedOption.getMode());
+                    }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
         } else {
             AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
                     R.style.Theme_SystemUI_Dialog)
@@ -306,8 +313,16 @@
                 // Start activity from the current foreground user to avoid creating a separate
                 // SystemUI process without access to recent tasks because it won't have
                 // WM Shell running inside.
+                // It is also important to make sure the shade is dismissed, otherwise users won't
+                // see the app selector.
                 mUserSelectingTask = true;
-                startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
+                mActivityStarter.startActivity(
+                        intent,
+                        /* dismissShade= */ true,
+                        /* animationController= */ null,
+                        /* showOverLockscreenWhenLocked= */ false,
+                        UserHandle.of(ActivityManager.getCurrentUser())
+                );
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error granting projection permission", e);
@@ -348,6 +363,16 @@
         }
     }
 
+    @Nullable
+    private MediaProjectionConfig getMediaProjectionConfig() {
+        Intent intent = getIntent();
+        if (intent == null) {
+            return null;
+        }
+        return intent.getParcelableExtra(
+                MediaProjectionManager.EXTRA_MEDIA_PROJECTION_CONFIG);
+    }
+
     private boolean isPartialScreenSharingEnabled() {
         return mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9f45f66..1e82d44 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -87,7 +87,6 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBar;
@@ -571,7 +570,6 @@
             SysUiState sysUiState,
             Provider<SceneInteractor> sceneInteractor,
             UserTracker userTracker,
-            ScreenLifecycle screenLifecycle,
             WakefulnessLifecycle wakefulnessLifecycle,
             UiEventLogger uiEventLogger,
             DisplayTracker displayTracker,
@@ -651,7 +649,6 @@
         // Listen for user setup
         mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
 
-        screenLifecycle.addObserver(mScreenLifecycleObserver);
         wakefulnessLifecycle.addObserver(mWakefulnessLifecycleObserver);
         // Connect to the service
         updateEnabledAndBinding();
@@ -923,60 +920,6 @@
         }
     }
 
-    private final ScreenLifecycle.Observer mScreenLifecycleObserver =
-            new ScreenLifecycle.Observer() {
-                /**
-                 * Notifies the Launcher that screen turned on and ready to use
-                 */
-                @Override
-                public void onScreenTurnedOn() {
-                    try {
-                        if (mOverviewProxy != null) {
-                            mOverviewProxy.onScreenTurnedOn();
-                        } else {
-                            Log.e(TAG_OPS,
-                                    "Failed to get overview proxy for screen turned on event.");
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG_OPS, "Failed to call onScreenTurnedOn()", e);
-                    }
-                }
-
-                /**
-                 * Notifies the Launcher that screen is starting to turn on.
-                 */
-                @Override
-                public void onScreenTurningOff() {
-                    try {
-                        if (mOverviewProxy != null) {
-                            mOverviewProxy.onScreenTurningOff();
-                        } else {
-                            Log.e(TAG_OPS,
-                                    "Failed to get overview proxy for screen turning off event.");
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG_OPS, "Failed to call onScreenTurningOff()", e);
-                    }
-                }
-
-                /**
-                 * Notifies the Launcher that screen is starting to turn on.
-                 */
-                @Override
-                public void onScreenTurningOn() {
-                    try {
-                        if (mOverviewProxy != null) {
-                            mOverviewProxy.onScreenTurningOn();
-                        } else {
-                            Log.e(TAG_OPS,
-                                    "Failed to get overview proxy for screen turning on event.");
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG_OPS, "Failed to call onScreenTurningOn()", e);
-                    }
-                }
-            };
-
     private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
             new WakefulnessLifecycle.Observer() {
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 398e64b..7147951 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene
 
+import com.android.systemui.keyguard.ui.view.LockscreenSceneModule
 import com.android.systemui.scene.domain.startable.SceneContainerStartableModule
 import com.android.systemui.scene.shared.model.SceneContainerConfigModule
 import com.android.systemui.scene.ui.composable.SceneModule
@@ -24,6 +25,7 @@
 @Module(
     includes =
         [
+            LockscreenSceneModule::class,
             SceneContainerConfigModule::class,
             SceneContainerStartableModule::class,
             SceneModule::class,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index 23894a3..7859fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -18,7 +18,9 @@
 import android.content.Context
 import android.os.Bundle
 import android.view.Gravity
+import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
 import android.view.ViewStub
 import android.view.WindowManager
 import android.widget.AdapterView
@@ -35,7 +37,7 @@
 
 /** Base permission dialog for screen share and recording */
 open class BaseScreenSharePermissionDialog(
-    context: Context?,
+    context: Context,
     private val screenShareOptions: List<ScreenShareOption>,
     private val appName: String?,
     @DrawableRes private val dialogIconDrawable: Int? = null,
@@ -82,14 +84,7 @@
         get() = context.getString(selectedScreenShareOption.warningText, appName)
 
     private fun initScreenShareSpinner() {
-        val options = screenShareOptions.map { context.getString(it.spinnerText) }.toTypedArray()
-        val adapter =
-            ArrayAdapter(
-                context.applicationContext,
-                R.layout.screen_share_dialog_spinner_text,
-                options
-            )
-        adapter.setDropDownViewResource(R.layout.screen_share_dialog_spinner_item_text)
+        val adapter = OptionsAdapter(context.applicationContext, screenShareOptions)
         screenShareModeSpinner = requireViewById(R.id.screen_share_mode_spinner)
         screenShareModeSpinner.adapter = adapter
         screenShareModeSpinner.onItemSelectedListener = this
@@ -131,3 +126,35 @@
         stub.inflate()
     }
 }
+
+private class OptionsAdapter(
+    context: Context,
+    private val options: List<ScreenShareOption>,
+) :
+    ArrayAdapter<String>(
+        context,
+        R.layout.screen_share_dialog_spinner_text,
+        options.map { context.getString(it.spinnerText) }
+    ) {
+
+    override fun isEnabled(position: Int): Boolean {
+        return options[position].spinnerDisabledText == null
+    }
+
+    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
+        val inflater = LayoutInflater.from(parent.context)
+        val view = inflater.inflate(R.layout.screen_share_dialog_spinner_item_text, parent, false)
+        val titleTextView = view.findViewById<TextView>(android.R.id.text1)
+        val errorTextView = view.findViewById<TextView>(android.R.id.text2)
+        titleTextView.text = getItem(position)
+        errorTextView.text = options[position].spinnerDisabledText
+        if (isEnabled(position)) {
+            errorTextView.visibility = View.GONE
+            titleTextView.isEnabled = true
+        } else {
+            errorTextView.visibility = View.VISIBLE
+            titleTextView.isEnabled = false
+        }
+        return view
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
index f4f5f66..8cbc4aab 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -16,16 +16,23 @@
 package com.android.systemui.screenrecord
 
 import android.content.Context
+import android.media.projection.MediaProjectionConfig
 import android.os.Bundle
 import com.android.systemui.R
 
 /** Dialog to select screen recording options */
 class MediaProjectionPermissionDialog(
-    context: Context?,
+    context: Context,
+    mediaProjectionConfig: MediaProjectionConfig?,
     private val onStartRecordingClicked: Runnable,
     private val onCancelClicked: Runnable,
     private val appName: String?
-) : BaseScreenSharePermissionDialog(context, createOptionList(appName), appName) {
+) :
+    BaseScreenSharePermissionDialog(
+        context,
+        createOptionList(context, appName, mediaProjectionConfig),
+        appName
+    ) {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         // TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
@@ -49,7 +56,11 @@
     }
 
     companion object {
-        private fun createOptionList(appName: String?): List<ScreenShareOption> {
+        private fun createOptionList(
+            context: Context,
+            appName: String?,
+            mediaProjectionConfig: MediaProjectionConfig?
+        ): List<ScreenShareOption> {
             val singleAppWarningText =
                 if (appName == null) {
                     R.string.media_projection_entry_cast_permission_dialog_warning_single_app
@@ -63,6 +74,19 @@
                     R.string.media_projection_entry_app_permission_dialog_warning_entire_screen
                 }
 
+            val singleAppDisabledText =
+                if (
+                    appName != null &&
+                        mediaProjectionConfig?.regionToCapture ==
+                            MediaProjectionConfig.CAPTURE_REGION_FIXED_DISPLAY
+                ) {
+                    context.getString(
+                        R.string.media_projection_entry_app_permission_dialog_single_app_disabled,
+                        appName
+                    )
+                } else {
+                    null
+                }
             return listOf(
                 ScreenShareOption(
                     mode = ENTIRE_SCREEN,
@@ -72,7 +96,8 @@
                 ScreenShareOption(
                     mode = SINGLE_APP,
                     spinnerText = R.string.screen_share_permission_dialog_option_single_app,
-                    warningText = singleAppWarningText
+                    warningText = singleAppWarningText,
+                    spinnerDisabledText = singleAppDisabledText,
                 )
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index fb99775..9c5da10 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -41,7 +41,7 @@
 
 /** Dialog to select screen recording options */
 class ScreenRecordPermissionDialog(
-    context: Context?,
+    context: Context,
     private val hostUserHandle: UserHandle,
     private val controller: RecordingController,
     private val activityStarter: ActivityStarter,
@@ -52,7 +52,7 @@
     BaseScreenSharePermissionDialog(
         context,
         createOptionList(),
-        null,
+        appName = null,
         R.drawable.ic_screenrecord,
         R.color.screenrecord_icon_color
     ) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
index 3d39fd8..ebf0dd2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
@@ -29,5 +29,6 @@
 class ScreenShareOption(
     @ScreenShareMode val mode: Int,
     @StringRes val spinnerText: Int,
-    @StringRes val warningText: Int
+    @StringRes val warningText: Int,
+    val spinnerDisabledText: String? = null,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 6c8190a..d0d37c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -25,6 +25,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
@@ -52,6 +53,7 @@
     private BrightnessMirrorController mMirrorController;
     private boolean mTracking;
     private final FalsingManager mFalsingManager;
+    private final UiEventLogger mUiEventLogger;
 
     private final Gefingerpoken mOnInterceptListener = new Gefingerpoken() {
         @Override
@@ -72,9 +74,11 @@
 
     BrightnessSliderController(
             BrightnessSliderView brightnessSliderView,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager,
+            UiEventLogger uiEventLogger) {
         super(brightnessSliderView);
         mFalsingManager = falsingManager;
+        mUiEventLogger = uiEventLogger;
     }
 
     /**
@@ -206,7 +210,7 @@
         @Override
         public void onStartTrackingTouch(SeekBar seekBar) {
             mTracking = true;
-
+            mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), false);
             }
@@ -220,7 +224,7 @@
         @Override
         public void onStopTrackingTouch(SeekBar seekBar) {
             mTracking = false;
-
+            mUiEventLogger.log(BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH);
             if (mListener != null) {
                 mListener.onChanged(mTracking, getValue(), true);
             }
@@ -237,10 +241,12 @@
     public static class Factory {
 
         private final FalsingManager mFalsingManager;
+        private final UiEventLogger mUiEventLogger;
 
         @Inject
-        public Factory(FalsingManager falsingManager) {
+        public Factory(FalsingManager falsingManager, UiEventLogger uiEventLogger) {
             mFalsingManager = falsingManager;
+            mUiEventLogger = uiEventLogger;
         }
 
         /**
@@ -250,11 +256,13 @@
          * @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated
          *                 hierarchy will not be attached
          */
-        public BrightnessSliderController create(Context context, @Nullable ViewGroup viewRoot) {
+        public BrightnessSliderController create(
+                Context context,
+                @Nullable ViewGroup viewRoot) {
             int layout = getLayout();
             BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
                     .inflate(layout, viewRoot, false);
-            return new BrightnessSliderController(root, mFalsingManager);
+            return new BrightnessSliderController(root, mFalsingManager, mUiEventLogger);
         }
 
         /** Get the layout to inflate based on what slider to use */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java
new file mode 100644
index 0000000..3a30880
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings.brightness;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+public enum BrightnessSliderEvent implements UiEventLogger.UiEventEnum {
+
+    @UiEvent(doc = "slider started to track touch")
+    SLIDER_STARTED_TRACKING_TOUCH(1472),
+    @UiEvent(doc = "slider stopped tracking touch")
+    SLIDER_STOPPED_TRACKING_TOUCH(1473);
+
+    private final int mId;
+
+    BrightnessSliderEvent(int id) {
+        mId = id;
+    }
+
+    @Override
+    public int getId() {
+        return mId;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
new file mode 100644
index 0000000..b34c3ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.gesture
+
+import android.content.Context
+import android.view.InputEvent
+import android.view.MotionEvent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.DisplayTracker
+import javax.inject.Inject
+
+/**
+ * A class to detect when a motion event happens. To be notified when the event is detected, add a
+ * callback via [addOnGestureDetectedCallback].
+ */
+@SysUISingleton
+class GesturePointerEventDetector @Inject constructor(
+        private val context: Context,
+        displayTracker: DisplayTracker
+) : GenericGestureDetector(
+        GesturePointerEventDetector::class.simpleName!!,
+        displayTracker.defaultDisplayId
+) {
+    override fun onInputEvent(ev: InputEvent) {
+        if (ev !is MotionEvent) {
+            return
+        }
+        // Pass all events to [gestureDetector], which will then notify [gestureListener] when a tap
+        // is detected.
+        onGestureDetected(ev)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
new file mode 100644
index 0000000..8505c5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventListener.kt
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2023 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.gesture
+
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.Region
+import android.hardware.display.DisplayManagerGlobal
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.util.Log
+import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.GestureDetector
+import android.view.InputDevice
+import android.view.InputEvent
+import android.view.MotionEvent
+import android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT
+import android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE
+import android.view.ViewRootImpl.CLIENT_TRANSIENT
+import android.widget.OverScroller
+import com.android.internal.R
+import com.android.systemui.CoreStartable
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Watches for gesture events that may trigger system bar related events and notify the registered
+ * callbacks. Add callback to this listener by calling {@link setCallbacks}.
+ */
+class GesturePointerEventListener
+@Inject
+constructor(context: Context, gestureDetector: GesturePointerEventDetector) : CoreStartable {
+    private val mContext: Context
+    private val mHandler = Handler(Looper.getMainLooper())
+    private var mGestureDetector: GesturePointerEventDetector
+    private var mFlingGestureDetector: GestureDetector? = null
+    private var mDisplayCutoutTouchableRegionSize = 0
+
+    // The thresholds for each edge of the display
+    private val mSwipeStartThreshold = Rect()
+    private var mSwipeDistanceThreshold = 0
+    private var mCallbacks: Callbacks? = null
+    private val mDownPointerId = IntArray(MAX_TRACKED_POINTERS)
+    private val mDownX = FloatArray(MAX_TRACKED_POINTERS)
+    private val mDownY = FloatArray(MAX_TRACKED_POINTERS)
+    private val mDownTime = LongArray(MAX_TRACKED_POINTERS)
+    var screenHeight = 0
+    var screenWidth = 0
+    private var mDownPointers = 0
+    private var mSwipeFireable = false
+    private var mDebugFireable = false
+    private var mMouseHoveringAtLeft = false
+    private var mMouseHoveringAtTop = false
+    private var mMouseHoveringAtRight = false
+    private var mMouseHoveringAtBottom = false
+    private var mLastFlingTime: Long = 0
+
+    init {
+        mContext = checkNull("context", context)
+        mGestureDetector = checkNull("gesture detector", gestureDetector)
+        onConfigurationChanged()
+    }
+
+    override fun start() {
+        if (!CLIENT_TRANSIENT) {
+            return
+        }
+        mGestureDetector.addOnGestureDetectedCallback(TAG) { ev -> onInputEvent(ev) }
+        mGestureDetector.startGestureListening()
+
+        mFlingGestureDetector =
+            object : GestureDetector(mContext, FlingGestureDetector(), mHandler) {}
+    }
+
+    fun onDisplayInfoChanged(info: DisplayInfo) {
+        screenWidth = info.logicalWidth
+        screenHeight = info.logicalHeight
+        onConfigurationChanged()
+    }
+
+    fun onConfigurationChanged() {
+        if (!CLIENT_TRANSIENT) {
+            return
+        }
+        val r = mContext.resources
+        val defaultThreshold = r.getDimensionPixelSize(R.dimen.system_gestures_start_threshold)
+        mSwipeStartThreshold[defaultThreshold, defaultThreshold, defaultThreshold] =
+            defaultThreshold
+        mSwipeDistanceThreshold = defaultThreshold
+        val display = DisplayManagerGlobal.getInstance().getRealDisplay(mContext.displayId)
+        val displayCutout = display.cutout
+        if (displayCutout != null) {
+            // Expand swipe start threshold such that we can catch touches that just start beyond
+            // the notch area
+            mDisplayCutoutTouchableRegionSize =
+                r.getDimensionPixelSize(R.dimen.display_cutout_touchable_region_size)
+            val bounds = displayCutout.boundingRectsAll
+            if (bounds[DisplayCutout.BOUNDS_POSITION_LEFT] != null) {
+                mSwipeStartThreshold.left =
+                    Math.max(
+                        mSwipeStartThreshold.left,
+                        bounds[DisplayCutout.BOUNDS_POSITION_LEFT]!!.width() +
+                            mDisplayCutoutTouchableRegionSize
+                    )
+            }
+            if (bounds[DisplayCutout.BOUNDS_POSITION_TOP] != null) {
+                mSwipeStartThreshold.top =
+                    Math.max(
+                        mSwipeStartThreshold.top,
+                        bounds[DisplayCutout.BOUNDS_POSITION_TOP]!!.height() +
+                            mDisplayCutoutTouchableRegionSize
+                    )
+            }
+            if (bounds[DisplayCutout.BOUNDS_POSITION_RIGHT] != null) {
+                mSwipeStartThreshold.right =
+                    Math.max(
+                        mSwipeStartThreshold.right,
+                        bounds[DisplayCutout.BOUNDS_POSITION_RIGHT]!!.width() +
+                            mDisplayCutoutTouchableRegionSize
+                    )
+            }
+            if (bounds[DisplayCutout.BOUNDS_POSITION_BOTTOM] != null) {
+                mSwipeStartThreshold.bottom =
+                    Math.max(
+                        mSwipeStartThreshold.bottom,
+                        bounds[DisplayCutout.BOUNDS_POSITION_BOTTOM]!!.height() +
+                            mDisplayCutoutTouchableRegionSize
+                    )
+            }
+        }
+        if (DEBUG)
+            Log.d(
+                TAG,
+                "mSwipeStartThreshold=$mSwipeStartThreshold" +
+                    " mSwipeDistanceThreshold=$mSwipeDistanceThreshold"
+            )
+    }
+
+    fun onInputEvent(ev: InputEvent) {
+        if (ev !is MotionEvent) {
+            return
+        }
+        if (DEBUG) Log.d(TAG, "Received motion event $ev")
+        if (ev.isTouchEvent) {
+            mFlingGestureDetector?.onTouchEvent(ev)
+        }
+        when (ev.actionMasked) {
+            MotionEvent.ACTION_DOWN -> {
+                mSwipeFireable = true
+                mDebugFireable = true
+                mDownPointers = 0
+                captureDown(ev, 0)
+                if (mMouseHoveringAtLeft) {
+                    mMouseHoveringAtLeft = false
+                    mCallbacks?.onMouseLeaveFromLeft()
+                }
+                if (mMouseHoveringAtTop) {
+                    mMouseHoveringAtTop = false
+                    mCallbacks?.onMouseLeaveFromTop()
+                }
+                if (mMouseHoveringAtRight) {
+                    mMouseHoveringAtRight = false
+                    mCallbacks?.onMouseLeaveFromRight()
+                }
+                if (mMouseHoveringAtBottom) {
+                    mMouseHoveringAtBottom = false
+                    mCallbacks?.onMouseLeaveFromBottom()
+                }
+                mCallbacks?.onDown()
+            }
+            MotionEvent.ACTION_POINTER_DOWN -> {
+                captureDown(ev, ev.actionIndex)
+                if (mDebugFireable) {
+                    mDebugFireable = ev.pointerCount < 5
+                    if (!mDebugFireable) {
+                        if (DEBUG) Log.d(TAG, "Firing debug")
+                        mCallbacks?.onDebug()
+                    }
+                }
+            }
+            MotionEvent.ACTION_MOVE ->
+                if (mSwipeFireable) {
+                    val trackpadSwipe = detectTrackpadThreeFingerSwipe(ev)
+                    mSwipeFireable = trackpadSwipe == TRACKPAD_SWIPE_NONE
+                    if (!mSwipeFireable) {
+                        if (trackpadSwipe == TRACKPAD_SWIPE_FROM_TOP) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromTop from trackpad")
+                            mCallbacks?.onSwipeFromTop()
+                        } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_BOTTOM) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromBottom from trackpad")
+                            mCallbacks?.onSwipeFromBottom()
+                        } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_RIGHT) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromRight from trackpad")
+                            mCallbacks?.onSwipeFromRight()
+                        } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_LEFT) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromLeft from trackpad")
+                            mCallbacks?.onSwipeFromLeft()
+                        }
+                    } else {
+                        val swipe = detectSwipe(ev)
+                        mSwipeFireable = swipe == SWIPE_NONE
+                        if (swipe == SWIPE_FROM_TOP) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromTop")
+                            mCallbacks?.onSwipeFromTop()
+                        } else if (swipe == SWIPE_FROM_BOTTOM) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromBottom")
+                            mCallbacks?.onSwipeFromBottom()
+                        } else if (swipe == SWIPE_FROM_RIGHT) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromRight")
+                            mCallbacks?.onSwipeFromRight()
+                        } else if (swipe == SWIPE_FROM_LEFT) {
+                            if (DEBUG) Log.d(TAG, "Firing onSwipeFromLeft")
+                            mCallbacks?.onSwipeFromLeft()
+                        }
+                    }
+                }
+            MotionEvent.ACTION_HOVER_MOVE ->
+                if (ev.isFromSource(InputDevice.SOURCE_MOUSE)) {
+                    val eventX = ev.x
+                    val eventY = ev.y
+                    if (!mMouseHoveringAtLeft && eventX == 0f) {
+                        mCallbacks?.onMouseHoverAtLeft()
+                        mMouseHoveringAtLeft = true
+                    } else if (mMouseHoveringAtLeft && eventX > 0) {
+                        mCallbacks?.onMouseLeaveFromLeft()
+                        mMouseHoveringAtLeft = false
+                    }
+                    if (!mMouseHoveringAtTop && eventY == 0f) {
+                        mCallbacks?.onMouseHoverAtTop()
+                        mMouseHoveringAtTop = true
+                    } else if (mMouseHoveringAtTop && eventY > 0) {
+                        mCallbacks?.onMouseLeaveFromTop()
+                        mMouseHoveringAtTop = false
+                    }
+                    if (!mMouseHoveringAtRight && eventX >= screenWidth - 1) {
+                        mCallbacks?.onMouseHoverAtRight()
+                        mMouseHoveringAtRight = true
+                    } else if (mMouseHoveringAtRight && eventX < screenWidth - 1) {
+                        mCallbacks?.onMouseLeaveFromRight()
+                        mMouseHoveringAtRight = false
+                    }
+                    if (!mMouseHoveringAtBottom && eventY >= screenHeight - 1) {
+                        mCallbacks?.onMouseHoverAtBottom()
+                        mMouseHoveringAtBottom = true
+                    } else if (mMouseHoveringAtBottom && eventY < screenHeight - 1) {
+                        mCallbacks?.onMouseLeaveFromBottom()
+                        mMouseHoveringAtBottom = false
+                    }
+                }
+            MotionEvent.ACTION_UP,
+            MotionEvent.ACTION_CANCEL -> {
+                mSwipeFireable = false
+                mDebugFireable = false
+                mCallbacks?.onUpOrCancel()
+            }
+            else -> if (DEBUG) Log.d(TAG, "Ignoring $ev")
+        }
+    }
+
+    fun setCallbacks(callbacks: Callbacks) {
+        mCallbacks = callbacks
+    }
+
+    private fun captureDown(event: MotionEvent, pointerIndex: Int) {
+        val pointerId = event.getPointerId(pointerIndex)
+        val i = findIndex(pointerId)
+        if (DEBUG) Log.d(TAG, "pointer $pointerId down pointerIndex=$pointerIndex trackingIndex=$i")
+        if (i != UNTRACKED_POINTER) {
+            mDownX[i] = event.getX(pointerIndex)
+            mDownY[i] = event.getY(pointerIndex)
+            mDownTime[i] = event.eventTime
+            if (DEBUG)
+                Log.d(TAG, "pointer " + pointerId + " down x=" + mDownX[i] + " y=" + mDownY[i])
+        }
+    }
+
+    protected fun currentGestureStartedInRegion(r: Region): Boolean {
+        return r.contains(mDownX[0].toInt(), mDownY[0].toInt())
+    }
+
+    private fun findIndex(pointerId: Int): Int {
+        for (i in 0 until mDownPointers) {
+            if (mDownPointerId[i] == pointerId) {
+                return i
+            }
+        }
+        if (mDownPointers == MAX_TRACKED_POINTERS || pointerId == MotionEvent.INVALID_POINTER_ID) {
+            return UNTRACKED_POINTER
+        }
+        mDownPointerId[mDownPointers++] = pointerId
+        return mDownPointers - 1
+    }
+
+    private fun detectTrackpadThreeFingerSwipe(move: MotionEvent): Int {
+        if (!isTrackpadThreeFingerSwipe(move)) {
+            return TRACKPAD_SWIPE_NONE
+        }
+        val dx = move.x - mDownX[0]
+        val dy = move.y - mDownY[0]
+        if (Math.abs(dx) < Math.abs(dy)) {
+            if (Math.abs(dy) > mSwipeDistanceThreshold) {
+                return if (dy > 0) TRACKPAD_SWIPE_FROM_TOP else TRACKPAD_SWIPE_FROM_BOTTOM
+            }
+        } else {
+            if (Math.abs(dx) > mSwipeDistanceThreshold) {
+                return if (dx > 0) TRACKPAD_SWIPE_FROM_LEFT else TRACKPAD_SWIPE_FROM_RIGHT
+            }
+        }
+        return TRACKPAD_SWIPE_NONE
+    }
+
+    private fun isTrackpadThreeFingerSwipe(event: MotionEvent): Boolean {
+        return (event.classification == CLASSIFICATION_MULTI_FINGER_SWIPE &&
+            event.getAxisValue(AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3f)
+    }
+    private fun detectSwipe(move: MotionEvent): Int {
+        val historySize = move.historySize
+        val pointerCount = move.pointerCount
+        for (p in 0 until pointerCount) {
+            val pointerId = move.getPointerId(p)
+            val i = findIndex(pointerId)
+            if (i != UNTRACKED_POINTER) {
+                for (h in 0 until historySize) {
+                    val time = move.getHistoricalEventTime(h)
+                    val x = move.getHistoricalX(p, h)
+                    val y = move.getHistoricalY(p, h)
+                    val swipe = detectSwipe(i, time, x, y)
+                    if (swipe != SWIPE_NONE) {
+                        return swipe
+                    }
+                }
+                val swipe = detectSwipe(i, move.eventTime, move.getX(p), move.getY(p))
+                if (swipe != SWIPE_NONE) {
+                    return swipe
+                }
+            }
+        }
+        return SWIPE_NONE
+    }
+
+    private fun detectSwipe(i: Int, time: Long, x: Float, y: Float): Int {
+        val fromX = mDownX[i]
+        val fromY = mDownY[i]
+        val elapsed = time - mDownTime[i]
+        if (DEBUG)
+            Log.d(
+                TAG,
+                "pointer " +
+                    mDownPointerId[i] +
+                    " moved (" +
+                    fromX +
+                    "->" +
+                    x +
+                    "," +
+                    fromY +
+                    "->" +
+                    y +
+                    ") in " +
+                    elapsed
+            )
+        if (
+            fromY <= mSwipeStartThreshold.top &&
+                y > fromY + mSwipeDistanceThreshold &&
+                elapsed < SWIPE_TIMEOUT_MS
+        ) {
+            return SWIPE_FROM_TOP
+        }
+        if (
+            fromY >= screenHeight - mSwipeStartThreshold.bottom &&
+                y < fromY - mSwipeDistanceThreshold &&
+                elapsed < SWIPE_TIMEOUT_MS
+        ) {
+            return SWIPE_FROM_BOTTOM
+        }
+        if (
+            fromX >= screenWidth - mSwipeStartThreshold.right &&
+                x < fromX - mSwipeDistanceThreshold &&
+                elapsed < SWIPE_TIMEOUT_MS
+        ) {
+            return SWIPE_FROM_RIGHT
+        }
+        return if (
+            fromX <= mSwipeStartThreshold.left &&
+                x > fromX + mSwipeDistanceThreshold &&
+                elapsed < SWIPE_TIMEOUT_MS
+        ) {
+            SWIPE_FROM_LEFT
+        } else SWIPE_NONE
+    }
+
+    fun dump(pw: PrintWriter, prefix: String) {
+        val inner = "$prefix  "
+        pw.println(prefix + TAG + ":")
+        pw.print(inner)
+        pw.print("mDisplayCutoutTouchableRegionSize=")
+        pw.println(mDisplayCutoutTouchableRegionSize)
+        pw.print(inner)
+        pw.print("mSwipeStartThreshold=")
+        pw.println(mSwipeStartThreshold)
+        pw.print(inner)
+        pw.print("mSwipeDistanceThreshold=")
+        pw.println(mSwipeDistanceThreshold)
+    }
+
+    private inner class FlingGestureDetector internal constructor() :
+        GestureDetector.SimpleOnGestureListener() {
+        private val mOverscroller: OverScroller = OverScroller(mContext)
+
+        override fun onSingleTapUp(e: MotionEvent): Boolean {
+            if (!mOverscroller.isFinished) {
+                mOverscroller.forceFinished(true)
+            }
+            return true
+        }
+
+        override fun onFling(
+            down: MotionEvent?,
+            up: MotionEvent,
+            velocityX: Float,
+            velocityY: Float
+        ): Boolean {
+            mOverscroller.computeScrollOffset()
+            val now = SystemClock.uptimeMillis()
+            if (mLastFlingTime != 0L && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) {
+                mOverscroller.forceFinished(true)
+            }
+            mOverscroller.fling(
+                0,
+                0,
+                velocityX.toInt(),
+                velocityY.toInt(),
+                Int.MIN_VALUE,
+                Int.MAX_VALUE,
+                Int.MIN_VALUE,
+                Int.MAX_VALUE
+            )
+            var duration = mOverscroller.duration
+            if (duration > MAX_FLING_TIME_MILLIS) {
+                duration = MAX_FLING_TIME_MILLIS
+            }
+            mLastFlingTime = now
+            mCallbacks?.onFling(duration)
+            return true
+        }
+    }
+
+    interface Callbacks {
+        fun onSwipeFromTop()
+        fun onSwipeFromBottom()
+        fun onSwipeFromRight()
+        fun onSwipeFromLeft()
+        fun onFling(durationMs: Int)
+        fun onDown()
+        fun onUpOrCancel()
+        fun onMouseHoverAtLeft()
+        fun onMouseHoverAtTop()
+        fun onMouseHoverAtRight()
+        fun onMouseHoverAtBottom()
+        fun onMouseLeaveFromLeft()
+        fun onMouseLeaveFromTop()
+        fun onMouseLeaveFromRight()
+        fun onMouseLeaveFromBottom()
+        fun onDebug()
+    }
+
+    companion object {
+        private const val TAG = "GesturePointerEventHandler"
+        private const val DEBUG = false
+        private const val SWIPE_TIMEOUT_MS: Long = 500
+        private const val MAX_TRACKED_POINTERS = 32 // max per input system
+        private const val UNTRACKED_POINTER = -1
+        private const val MAX_FLING_TIME_MILLIS = 5000
+        private const val SWIPE_NONE = 0
+        private const val SWIPE_FROM_TOP = 1
+        private const val SWIPE_FROM_BOTTOM = 2
+        private const val SWIPE_FROM_RIGHT = 3
+        private const val SWIPE_FROM_LEFT = 4
+        private const val TRACKPAD_SWIPE_NONE = 0
+        private const val TRACKPAD_SWIPE_FROM_TOP = 1
+        private const val TRACKPAD_SWIPE_FROM_BOTTOM = 2
+        private const val TRACKPAD_SWIPE_FROM_RIGHT = 3
+        private const val TRACKPAD_SWIPE_FROM_LEFT = 4
+
+        private fun <T> checkNull(name: String, arg: T?): T {
+            requireNotNull(arg) { "$name must not be null" }
+            return arg
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 62a0d13..5c2f9a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -39,7 +39,10 @@
 
     override fun attach(pipeline: NotifPipeline) {
         pipeline.addOnAfterRenderListListener(::onAfterRenderList)
-        groupExpansionManagerImpl.attach(pipeline)
+        // TODO(b/282865576): This has an issue where it makes changes to some groups without
+        // notifying listeners. To be fixed in QPR, but for now let's comment it out to avoid the
+        // group expansion bug.
+        // groupExpansionManagerImpl.attach(pipeline)
     }
 
     fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index 5d33804..46af03a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -67,29 +67,18 @@
      * Cleanup entries from mExpandedGroups that no longer exist in the pipeline.
      */
     private final OnBeforeRenderListListener mNotifTracker = (entries) -> {
-        if (mExpandedGroups.isEmpty()) {
-            return; // nothing to do
-        }
-
         final Set<NotificationEntry> renderingSummaries = new HashSet<>();
         for (ListEntry entry : entries) {
             if (entry instanceof GroupEntry) {
                 renderingSummaries.add(entry.getRepresentativeEntry());
             }
         }
-
-        // Create a copy of mExpandedGroups so we can modify it in a thread-safe way.
-        final var currentExpandedGroups = new HashSet<>(mExpandedGroups);
-        for (NotificationEntry entry : currentExpandedGroups) {
-            setExpanded(entry, renderingSummaries.contains(entry));
-        }
+        mExpandedGroups.removeIf(expandedGroup -> !renderingSummaries.contains(expandedGroup));
     };
 
     public void attach(NotifPipeline pipeline) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
-            mDumpManager.registerDumpable(this);
-            pipeline.addOnBeforeRenderListListener(mNotifTracker);
-        }
+        mDumpManager.registerDumpable(this);
+        pipeline.addOnBeforeRenderListListener(mNotifTracker);
     }
 
     @Override
@@ -105,24 +94,11 @@
     @Override
     public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
         final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
-        setExpanded(groupSummary, expanded);
-    }
-
-    /**
-     * Add or remove {@code entry} to/from {@code mExpandedGroups} and notify listeners if
-     * something changed. This assumes that {@code entry} is a group summary.
-     * <p>
-     * TODO(b/293434635): Currently, in spite of its docs,
-     * {@code mGroupMembershipManager.getGroupSummary(entry)} returns null if {@code entry} is
-     * already a summary. Instead of needing this helper method to bypass that, we probably want to
-     * move this code back to {@code setGroupExpanded} and use that everywhere.
-     */
-    private void setExpanded(NotificationEntry entry, boolean expanded) {
         boolean changed;
         if (expanded) {
-            changed = mExpandedGroups.add(entry);
+            changed = mExpandedGroups.add(groupSummary);
         } else {
-            changed = mExpandedGroups.remove(entry);
+            changed = mExpandedGroups.remove(groupSummary);
         }
 
         // Only notify listeners if something changed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
index 4429939..d10b556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt
@@ -21,29 +21,25 @@
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
-import com.android.systemui.Dumpable
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
 import com.android.systemui.statusbar.notification.row.NotificationRowModule.NOTIF_REMOTEVIEWS_FACTORIES
-import com.android.systemui.util.asIndenting
-import com.android.systemui.util.withIncreasedIndent
-import java.io.PrintWriter
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import javax.inject.Named
 
 /**
  * Implementation of [NotifLayoutInflaterFactory]. This class uses a set of
  * [NotifRemoteViewsFactory] objects to create replacement views for Notification RemoteViews.
  */
-open class NotifLayoutInflaterFactory
-@Inject
+class NotifLayoutInflaterFactory
+@AssistedInject
 constructor(
-    dumpManager: DumpManager,
+    @Assisted private val row: ExpandableNotificationRow,
+    @Assisted @InflationFlag val layoutType: Int,
     @Named(NOTIF_REMOTEVIEWS_FACTORIES)
     private val remoteViewsFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory>
-) : LayoutInflater.Factory2, Dumpable {
-    init {
-        dumpManager.registerNormalDumpable(TAG, this)
-    }
+) : LayoutInflater.Factory2 {
 
     override fun onCreateView(
         parent: View?,
@@ -51,41 +47,32 @@
         context: Context,
         attrs: AttributeSet
     ): View? {
-        var view: View? = null
         var handledFactory: NotifRemoteViewsFactory? = null
+        var result: View? = null
         for (layoutFactory in remoteViewsFactories) {
-            view = layoutFactory.instantiate(parent, name, context, attrs)
-            if (view != null) {
+            layoutFactory.instantiate(row, layoutType, parent, name, context, attrs)?.run {
                 check(handledFactory == null) {
-                    "${layoutFactory.javaClass.name} tries to produce view. However, " +
-                        "${handledFactory?.javaClass?.name} produced view for $name before."
+                    "$layoutFactory tries to produce name:$name with type:$layoutType. " +
+                        "However, $handledFactory produced view for $name before."
                 }
                 handledFactory = layoutFactory
+                result = this
             }
         }
-        logOnCreateView(name, view, handledFactory)
-        return view
+        logOnCreateView(name, result, handledFactory)
+        return result
     }
 
     override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? =
         onCreateView(null, name, context, attrs)
 
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        val indentingPW = pw.asIndenting()
-
-        indentingPW.appendLine("$TAG ReplacementFactories:")
-        indentingPW.withIncreasedIndent {
-            remoteViewsFactories.forEach { indentingPW.appendLine(it.javaClass.simpleName) }
-        }
-    }
-
     private fun logOnCreateView(
         name: String,
         replacedView: View?,
         factory: NotifRemoteViewsFactory?
     ) {
         if (SPEW && replacedView != null && factory != null) {
-            Log.d(TAG, "$factory produced view for $name: $replacedView")
+            Log.d(TAG, "$factory produced $replacedView for name:$name with type:$layoutType")
         }
     }
 
@@ -93,4 +80,12 @@
         private const val TAG = "NotifLayoutInflaterFac"
         private val SPEW = Log.isLoggable(TAG, Log.VERBOSE)
     }
+
+    @AssistedFactory
+    interface Provider {
+        fun provide(
+            row: ExpandableNotificationRow,
+            @InflationFlag layoutType: Int
+        ): NotifLayoutInflaterFactory
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
index eebd4d4..bf740e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt
@@ -19,10 +19,18 @@
 import android.content.Context
 import android.util.AttributeSet
 import android.view.View
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
 
 /** Interface used to create replacement view instances in Notification RemoteViews. */
 interface NotifRemoteViewsFactory {
 
     /** return the replacement view instance for the given view name */
-    fun instantiate(parent: View?, name: String, context: Context, attrs: AttributeSet): View?
+    fun instantiate(
+        row: ExpandableNotificationRow,
+        @InflationFlag layoutType: Int,
+        parent: View?,
+        name: String,
+        context: Context,
+        attrs: AttributeSet
+    ): View?
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 0ad77bd..86f545d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -79,7 +79,7 @@
     private final ConversationNotificationProcessor mConversationProcessor;
     private final Executor mBgExecutor;
     private final SmartReplyStateInflater mSmartReplyStateInflater;
-    private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
+    private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
 
     @Inject
     NotificationContentInflater(
@@ -89,14 +89,14 @@
             MediaFeatureFlag mediaFeatureFlag,
             @Background Executor bgExecutor,
             SmartReplyStateInflater smartRepliesInflater,
-            NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+            NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
         mRemoteViewCache = remoteViewCache;
         mRemoteInputManager = remoteInputManager;
         mConversationProcessor = conversationProcessor;
         mIsMediaInQS = mediaFeatureFlag.getEnabled();
         mBgExecutor = bgExecutor;
         mSmartReplyStateInflater = smartRepliesInflater;
-        mNotifLayoutInflaterFactory = notifLayoutInflaterFactory;
+        mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
     }
 
     @Override
@@ -141,7 +141,7 @@
                 mRemoteInputManager.getRemoteViewsOnClickHandler(),
                 mIsMediaInQS,
                 mSmartReplyStateInflater,
-                mNotifLayoutInflaterFactory);
+                mNotifLayoutInflaterFactoryProvider);
         if (mInflateSynchronously) {
             task.onPostExecute(task.doInBackground());
         } else {
@@ -165,7 +165,8 @@
                 bindParams.usesIncreasedHeight,
                 bindParams.usesIncreasedHeadsUpHeight,
                 packageContext,
-                mNotifLayoutInflaterFactory);
+                row,
+                mNotifLayoutInflaterFactoryProvider);
 
         result = inflateSmartReplyViews(result, reInflateFlags, entry,
                 row.getContext(), packageContext,
@@ -304,7 +305,8 @@
     private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
             Notification.Builder builder, boolean isLowPriority, boolean usesIncreasedHeight,
             boolean usesIncreasedHeadsUpHeight, Context packageContext,
-            NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+            ExpandableNotificationRow row,
+            NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
         InflationProgress result = new InflationProgress();
 
         if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
@@ -322,7 +324,7 @@
         if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
             result.newPublicView = builder.makePublicContentView(isLowPriority);
         }
-        setNotifsViewsInflaterFactory(result, notifLayoutInflaterFactory);
+        setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider);
         result.packageContext = packageContext;
         result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
         result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
@@ -331,12 +333,16 @@
     }
 
     private static void setNotifsViewsInflaterFactory(InflationProgress result,
-            NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
-        setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactory);
+            ExpandableNotificationRow row,
+            NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
+        setRemoteViewsInflaterFactory(result.newContentView,
+                notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED));
         setRemoteViewsInflaterFactory(result.newExpandedView,
-                notifLayoutInflaterFactory);
-        setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactory);
-        setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactory);
+                notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_EXPANDED));
+        setRemoteViewsInflaterFactory(result.newHeadsUpView,
+                notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP));
+        setRemoteViewsInflaterFactory(result.newPublicView,
+                notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC));
     }
 
     private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews,
@@ -821,7 +827,7 @@
         private final ConversationNotificationProcessor mConversationProcessor;
         private final boolean mIsMediaInQS;
         private final SmartReplyStateInflater mSmartRepliesInflater;
-        private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
+        private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
 
         private AsyncInflationTask(
                 Executor bgExecutor,
@@ -838,7 +844,7 @@
                 RemoteViews.InteractionHandler remoteViewClickHandler,
                 boolean isMediaFlagEnabled,
                 SmartReplyStateInflater smartRepliesInflater,
-                NotifLayoutInflaterFactory notifLayoutInflaterFactory) {
+                NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) {
             mEntry = entry;
             mRow = row;
             mBgExecutor = bgExecutor;
@@ -854,7 +860,7 @@
             mCallback = callback;
             mConversationProcessor = conversationProcessor;
             mIsMediaInQS = isMediaFlagEnabled;
-            mNotifLayoutInflaterFactory = notifLayoutInflaterFactory;
+            mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
             entry.setInflationTask(this);
         }
 
@@ -898,8 +904,8 @@
                 }
                 InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
                         recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight,
-                        mUsesIncreasedHeadsUpHeight, packageContext,
-                        mNotifLayoutInflaterFactory);
+                        mUsesIncreasedHeadsUpHeight, packageContext, mRow,
+                        mNotifLayoutInflaterFactoryProvider);
                 InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState();
                 InflationProgress result = inflateSmartReplyViews(
                         inflationProgress,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
index 51e4537..4ace194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
@@ -21,6 +21,7 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerExecutor;
+import android.os.Trace;
 
 import androidx.annotation.MainThread;
 import androidx.annotation.NonNull;
@@ -69,6 +70,7 @@
         mContentObserver = new ContentObserver(mBackgroundHandler) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
+                Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".ContentObserver.onChange");
                 super.onChange(selfChange, uri);
                 synchronized (mListeners) {
                     if (mListeners.containsKey(uri)) {
@@ -79,12 +81,15 @@
                         }
                     }
                 }
+                Trace.traceEnd(Trace.TRACE_TAG_APP);
             }
         };
 
         mCurrentUserTrackerCallback = new UserTracker.Callback() {
             @Override
             public void onUserChanged(int newUser, Context userContext) {
+                Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".UserTracker.Callback.onUserChanged");
+
                 synchronized (mListeners) {
                     if (mListeners.size() > 0) {
                         mSecureSettings.unregisterContentObserver(mContentObserver);
@@ -94,6 +99,7 @@
                         }
                     }
                 }
+                Trace.traceEnd(Trace.TRACE_TAG_APP);
             }
         };
         mUserTracker.addCallback(
@@ -113,6 +119,7 @@
         if (uri == null || listener == null) {
             return;
         }
+        Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".addCallback");
         synchronized (mListeners) {
             ArrayList<Listener> currentListeners = mListeners.get(uri);
             if (currentListeners == null) {
@@ -132,10 +139,12 @@
             String value = getCurrentSettingValue(uri, userId);
             mMainHandler.post(() -> listener.onSettingChanged(uri, userId, value));
         });
-
+        Trace.traceEnd(Trace.TRACE_TAG_APP);
     }
 
     public void removeCallback(Uri uri, Listener listener) {
+        Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".removeCallback");
+
         synchronized (mListeners) {
             ArrayList<Listener> currentListeners = mListeners.get(uri);
 
@@ -150,10 +159,13 @@
                 mSecureSettings.unregisterContentObserver(mContentObserver);
             }
         }
+        Trace.traceEnd(Trace.TRACE_TAG_APP);
     }
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+        Trace.traceBegin(Trace.TRACE_TAG_APP, TAG + ".dump");
+
         synchronized (mListeners) {
             pw.println("Settings Uri Listener List:");
             for (Uri uri : mListeners.keySet()) {
@@ -163,6 +175,7 @@
                 }
             }
         }
+        Trace.traceEnd(Trace.TRACE_TAG_APP);
     }
 
     private String getCurrentSettingValue(Uri uri, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
index c276827..96547db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt
@@ -23,10 +23,13 @@
 import com.android.internal.widget.ConversationLayout
 import com.android.internal.widget.ImageFloatingTextView
 import com.android.internal.widget.MessagingLayout
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
 import javax.inject.Inject
 
 class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory {
     override fun instantiate(
+        row: ExpandableNotificationRow,
+        @InflationFlag layoutType: Int,
         parent: View?,
         name: String,
         context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ea57eb4..27b8406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -92,6 +92,8 @@
 import com.android.systemui.unfold.FoldAodAnimationController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -101,7 +103,6 @@
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
 import kotlinx.coroutines.CoroutineDispatcher;
 
 /**
@@ -450,14 +451,6 @@
         }
     }
 
-    private KeyguardStateController.Callback mKeyguardStateControllerCallback =
-            new KeyguardStateController.Callback() {
-                @Override
-                public void onUnlockedChanged() {
-                    updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide());
-                }
-            };
-
     private void registerListeners() {
         mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
         mStatusBarStateController.addCallback(this);
@@ -471,7 +464,6 @@
             mDockManager.addListener(mDockEventListener);
             mIsDocked = mDockManager.isDocked();
         }
-        mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
 
         if (mFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
             mShadeViewController.postToView(() ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index cd1afc7..37f032b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -226,7 +226,7 @@
         val builder = InteractionJankMonitor.Configuration.Builder
             .withView(
                     InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD,
-                    notifShadeWindowControllerLazy.get().windowRootView
+                    checkNotNull(notifShadeWindowControllerLazy.get().windowRootView)
             )
             .setTag(statusBarStateControllerImpl.getClockId())
 
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
index 2325acf..f5decaa 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
@@ -17,11 +17,13 @@
 package com.android.systemui.unfold
 
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FoldProvider
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener
 import java.util.concurrent.Executor
 
-class UnfoldProgressProvider(private val unfoldProgressProvider: UnfoldTransitionProgressProvider) :
+class UnfoldProgressProvider(private val unfoldProgressProvider: UnfoldTransitionProgressProvider,
+        private val foldProvider: FoldProvider) :
     ShellUnfoldProgressProvider {
 
     override fun addListener(executor: Executor, listener: UnfoldListener) {
@@ -39,5 +41,11 @@
                     executor.execute { listener.onStateChangeFinished() }
                 }
             })
+
+        foldProvider.registerCallback(object : FoldProvider.FoldCallback {
+            override fun onFoldUpdated(isFolded: Boolean) {
+                listener.onFoldStateChanged(isFolded)
+            }
+        }, executor)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 992b022..ed3eacd 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -118,6 +118,7 @@
     @Singleton
     fun provideShellProgressProvider(
         config: UnfoldTransitionConfig,
+        foldProvider: FoldProvider,
         provider: Provider<Optional<UnfoldTransitionProgressProvider>>,
         @Named(UNFOLD_ONLY_PROVIDER)
         unfoldOnlyProvider: Provider<Optional<UnfoldTransitionProgressProvider>>
@@ -135,8 +136,9 @@
                 null
             }
 
-        return resultingProvider?.get()?.orElse(null)?.let(::UnfoldProgressProvider)
-            ?: ShellUnfoldProgressProvider.NO_PROVIDER
+        return resultingProvider?.get()?.orElse(null)?.let {
+            unfoldProgressProvider -> UnfoldProgressProvider(unfoldProgressProvider, foldProvider)
+        } ?: ShellUnfoldProgressProvider.NO_PROVIDER
     }
 
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 954765c..8cfa555 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.user.data.model.SelectedUserModel
 import com.android.systemui.user.data.model.SelectionStatus
@@ -49,7 +48,6 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
@@ -77,9 +75,6 @@
     /** [UserInfo] of the currently-selected user. */
     val selectedUserInfo: Flow<UserInfo>
 
-    /** Whether user switching is currently in progress. */
-    val userSwitchingInProgress: Flow<Boolean>
-
     /** User ID of the main user. */
     val mainUserId: Int
 
@@ -162,10 +157,6 @@
     private var _isGuestUserResetting: Boolean = false
     override var isGuestUserResetting: Boolean = _isGuestUserResetting
 
-    private val _isUserSwitchingInProgress = MutableStateFlow(false)
-    override val userSwitchingInProgress: Flow<Boolean>
-        get() = _isUserSwitchingInProgress
-
     override val isGuestUserCreationScheduled = AtomicBoolean()
 
     override val isStatusBarUserChipEnabled: Boolean =
@@ -175,12 +166,6 @@
 
     override var isRefreshUsersPaused: Boolean = false
 
-    init {
-        if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
-            observeUserSwitching()
-        }
-    }
-
     override val selectedUser: StateFlow<SelectedUserModel> = run {
         // Some callbacks don't modify the selection status, so maintain the current value.
         var currentSelectionStatus = SelectionStatus.SELECTION_COMPLETE
@@ -259,28 +244,6 @@
         return _userSwitcherSettings.value.isUserSwitcherEnabled
     }
 
-    private fun observeUserSwitching() {
-        conflatedCallbackFlow {
-                val callback =
-                    object : UserTracker.Callback {
-                        override fun onUserChanging(newUser: Int, userContext: Context) {
-                            trySendWithFailureLogging(true, TAG, "userSwitching started")
-                        }
-
-                        override fun onUserChanged(newUserId: Int, userContext: Context) {
-                            trySendWithFailureLogging(false, TAG, "userSwitching completed")
-                        }
-                    }
-                tracker.addCallback(callback, mainDispatcher.asExecutor())
-                trySendWithFailureLogging(false, TAG, "initial value defaulting to false")
-                awaitClose { tracker.removeCallback(callback) }
-            }
-            .onEach { _isUserSwitchingInProgress.value = it }
-            // TODO (b/262838215), Make this stateIn and initialize directly in field declaration
-            //  once the flag is launched
-            .launchIn(applicationScope)
-    }
-
     private suspend fun getSettings(): UserSwitcherSettingsModel {
         return withContext(backgroundDispatcher) {
             val isSimpleUserSwitcher =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
index fcc4040..0da7b4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
@@ -17,14 +17,19 @@
 
 package com.android.systemui.biometrics.data.repository
 
+import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE
+import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_PERMANENT
+import android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED
 import android.hardware.biometrics.SensorProperties
 import android.hardware.face.FaceManager
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.LockoutMode
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -74,18 +79,52 @@
     @Test
     fun providesTheValuePassedToTheAuthenticatorsRegisteredCallback() {
         testScope.runTest {
-            val sensor by collectLastValue(underTest.sensorInfo)
             runCurrent()
             verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
 
             callback.value.onAllAuthenticatorsRegistered(
                 listOf(createSensorProperties(1, SensorProperties.STRENGTH_STRONG))
             )
+            runCurrent()
 
+            val sensor by collectLastValue(underTest.sensorInfo)
             assertThat(sensor).isEqualTo(FaceSensorInfo(1, SensorStrength.STRONG))
         }
     }
 
+    @Test
+    fun providesTheNoneLockoutModeWhenFaceManagerIsNotAvailable() =
+        testScope.runTest {
+            underTest = createRepository(null)
+
+            assertThat(underTest.getLockoutMode(-1)).isEqualTo(LockoutMode.NONE)
+        }
+
+    @Test
+    fun providesTheLockoutModeFromFaceManager() =
+        testScope.runTest {
+            val sensorId = 99
+            val userId = 999
+            runCurrent()
+            verify(faceManager).addAuthenticatorsRegisteredCallback(callback.capture())
+            callback.value.onAllAuthenticatorsRegistered(
+                listOf(createSensorProperties(sensorId, SensorProperties.STRENGTH_STRONG))
+            )
+            runCurrent()
+
+            whenever(faceManager.getLockoutModeForUser(sensorId, userId))
+                .thenReturn(BIOMETRIC_LOCKOUT_TIMED)
+            assertThat(underTest.getLockoutMode(userId)).isEqualTo(LockoutMode.TIMED)
+
+            whenever(faceManager.getLockoutModeForUser(sensorId, userId))
+                .thenReturn(BIOMETRIC_LOCKOUT_PERMANENT)
+            assertThat(underTest.getLockoutMode(userId)).isEqualTo(LockoutMode.PERMANENT)
+
+            whenever(faceManager.getLockoutModeForUser(sensorId, userId))
+                .thenReturn(BIOMETRIC_LOCKOUT_NONE)
+            assertThat(underTest.getLockoutMode(userId)).isEqualTo(LockoutMode.NONE)
+        }
+
     private fun createSensorProperties(id: Int, strength: Int) =
         FaceSensorPropertiesInternal(id, strength, 0, emptyList(), 1, false, false, false)
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
index da55d5a..95b72d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -28,7 +28,7 @@
 @SmallTest
 @RunWith(Parameterized::class)
 class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
-    val underTest = BoundingBoxOverlapDetector()
+    val underTest = BoundingBoxOverlapDetector(1f)
 
     @Test
     fun isGoodOverlap() {
@@ -83,7 +83,7 @@
         GESTURE_START
     )
 
-private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
+private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 400 /* bottom */)
 private val OVERLAY = Rect(0 /* left */, 100 /* top */, 400 /* right */, 600 /* bottom */)
 
 private fun genTestCases(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 11b0b07..47084c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -312,10 +312,13 @@
             assertThat(message).isEqualTo(PromptMessage.Empty)
             assertThat(messageVisible).isFalse()
         }
+        val clearIconError = !restart
         assertThat(legacyState)
             .isEqualTo(
                 if (restart) {
                     AuthBiometricView.STATE_AUTHENTICATING
+                } else if (clearIconError) {
+                    AuthBiometricView.STATE_IDLE
                 } else {
                     AuthBiometricView.STATE_HELP
                 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 8236165..d4bba72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -29,6 +29,8 @@
 import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
 import com.android.systemui.bouncer.ui.BouncerView
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.DismissCallbackRegistry
@@ -156,4 +158,24 @@
         assertThat(isShowing).isEqualTo(false)
         job.cancel()
     }
+
+    @Test
+    fun keyguardPosition_noValueSet_emptyByDefault() = runTest {
+        val positionValues by collectValues(underTest.keyguardPosition)
+
+        runCurrent()
+
+        assertThat(positionValues).isEmpty()
+    }
+
+    @Test
+    fun keyguardPosition_valueSet_returnsValue() = runTest {
+        val position by collectLastValue(underTest.keyguardPosition)
+        runCurrent()
+
+        repository.setKeyguardPosition(123f)
+        runCurrent()
+
+        assertThat(position).isEqualTo(123f)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 64b9470..f0dbaf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -71,6 +71,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.phone.FakeKeyguardStateController
 import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.KotlinArgumentCaptor
 import com.android.systemui.util.mockito.captureMany
@@ -295,6 +296,7 @@
     fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
         testScope.runTest {
             initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
 
             underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
@@ -311,6 +313,7 @@
         testScope.runTest {
             initCollectors()
             verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
+            allPreconditionsToRunFaceAuthAreTrue()
 
             underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
@@ -364,6 +367,7 @@
     fun cancelStopsFaceAuthentication() =
         testScope.runTest {
             initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
 
             underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
@@ -417,6 +421,7 @@
     fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
         testScope.runTest {
             initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
 
             underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
@@ -492,6 +497,7 @@
             )
             underTest = createDeviceEntryFaceAuthRepositoryImpl()
             initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
 
             underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
             faceAuthenticateIsCalled()
@@ -803,7 +809,10 @@
 
             assertThat(authenticated()).isTrue()
 
-            fakeUserRepository.setUserSwitching(true)
+            fakeUserRepository.setSelectedUserInfo(
+                primaryUser,
+                SelectionStatus.SELECTION_IN_PROGRESS
+            )
 
             assertThat(authenticated()).isFalse()
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 93f208e..ec11573 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import android.content.pm.UserInfo
 import android.hardware.biometrics.BiometricFaceConstants
 import android.os.Handler
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -25,6 +26,8 @@
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
+import com.android.systemui.biometrics.shared.model.LockoutMode
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -48,6 +51,7 @@
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -76,6 +80,7 @@
     private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
     private lateinit var faceAuthRepository: FakeDeviceEntryFaceAuthRepository
     private lateinit var fakeUserRepository: FakeUserRepository
+    private lateinit var facePropertyRepository: FakeFacePropertyRepository
     private lateinit var fakeDeviceEntryFingerprintAuthRepository:
         FakeDeviceEntryFingerprintAuthRepository
 
@@ -101,6 +106,8 @@
 
         fakeDeviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
         fakeUserRepository = FakeUserRepository()
+        fakeUserRepository.setUserInfos(listOf(primaryUser, secondaryUser))
+        facePropertyRepository = FakeFacePropertyRepository()
         underTest =
             SystemUIKeyguardFaceAuthInteractor(
                 mContext,
@@ -136,6 +143,7 @@
                 keyguardUpdateMonitor,
                 fakeDeviceEntryFingerprintAuthRepository,
                 fakeUserRepository,
+                facePropertyRepository,
             )
     }
 
@@ -220,9 +228,12 @@
         testScope.runTest {
             underTest.start()
 
-            fakeUserRepository.setUserSwitching(false)
+            fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
             runCurrent()
-            fakeUserRepository.setUserSwitching(true)
+            fakeUserRepository.setSelectedUserInfo(
+                secondaryUser,
+                SelectionStatus.SELECTION_IN_PROGRESS
+            )
             runCurrent()
 
             assertThat(faceAuthRepository.isFaceAuthPaused()).isTrue()
@@ -234,17 +245,27 @@
             underTest.start()
 
             // previously running
-            fakeUserRepository.setUserSwitching(true)
+            fakeUserRepository.setSelectedUserInfo(
+                primaryUser,
+                SelectionStatus.SELECTION_IN_PROGRESS
+            )
             runCurrent()
-            fakeUserRepository.setUserSwitching(false)
+            bouncerRepository.setPrimaryShow(true)
+
+            facePropertyRepository.setLockoutMode(secondaryUser.id, LockoutMode.TIMED)
+            fakeUserRepository.setSelectedUserInfo(
+                secondaryUser,
+                SelectionStatus.SELECTION_COMPLETE
+            )
             runCurrent()
 
             assertThat(faceAuthRepository.isFaceAuthPaused()).isFalse()
+            assertThat(faceAuthRepository.wasDisabled).isTrue()
 
             runCurrent()
             assertThat(faceAuthRepository.runningAuthRequest.value!!.first)
                 .isEqualTo(FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING)
-            assertThat(faceAuthRepository.runningAuthRequest.value!!.second).isEqualTo(true)
+            assertThat(faceAuthRepository.runningAuthRequest.value!!.second).isEqualTo(false)
         }
 
     @Test
@@ -259,7 +280,7 @@
 
             runCurrent()
             assertThat(faceAuthRepository.runningAuthRequest.value)
-                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, true))
+                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, false))
         }
 
     @Test
@@ -387,4 +408,11 @@
 
             assertThat(faceAuthRepository.wasDisabled).isTrue()
         }
+
+    companion object {
+        private const val primaryUserId = 1
+        private val primaryUser = UserInfo(primaryUserId, "test user", UserInfo.FLAG_PRIMARY)
+
+        private val secondaryUser = UserInfo(2, "secondary user", 0)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index e0ae0c3..a3f7fc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -131,12 +131,14 @@
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+    fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
         keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(shadeController).animateCollapseShadeForced()
     }
 
     @Test
@@ -145,48 +147,42 @@
         whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
+
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(shadeController).animateCollapseShadeForced()
     }
 
     @Test
-    fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_doNothing() {
+    fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
         keyguardInteractorWithDependencies.repository.setWakefulnessModel(asleepWakefulnessMode)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
         whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
 
-        verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
     }
 
     @Test
-    fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_showsPrimaryBouncer() {
+    fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
         keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
 
-        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
-    }
+        // action down: does NOT collapse the shade
+        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
+        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+        verify(shadeController, never()).animateCollapseShadeForced()
 
-    @Test
-    fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
-        keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
-        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
-        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
-    }
-
-    @Test
-    fun dispatchKeyEvent_enterActionUp_interactiveKeyguard_showsPrimaryBouncer() {
-        keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
-        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
-        verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
-    }
-
-    @Test
-    fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
-        keyguardInteractorWithDependencies.repository.setWakefulnessModel(awakeWakefulnessMode)
-        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
-        verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
+        // action up: collapses the shade
+        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+        verify(shadeController).animateCollapseShadeForced()
     }
 
     @Test
@@ -256,42 +252,4 @@
             .isFalse()
         verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
     }
-
-    private fun verifyActionUpCollapsesTheShade(keycode: Int) {
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
-
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(shadeController).animateCollapseShadeForced()
-    }
-
-    private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
-        // action down: does NOT collapse the shade
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
-        // action up: collapses the shade
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
-        verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
-    }
-
-    private fun verifyActionsDoNothing(keycode: Int) {
-        // action down: does nothing
-        val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
-        assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
-        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
-        // action up: doesNothing
-        val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
-        assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
-        verify(shadeController, never()).animateCollapseShadeForced()
-        verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 23f243c..a9f288d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -17,10 +17,8 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import androidx.test.filters.SmallTest
-import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.model.AuthenticationMethodModel
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.scene.shared.model.SceneKey
@@ -48,7 +46,6 @@
 
     private val underTest =
         LockscreenSceneViewModel(
-            applicationScope = testScope.backgroundScope,
             authenticationInteractor = authenticationInteractor,
             bouncerInteractor =
                 utils.bouncerInteractor(
@@ -58,32 +55,6 @@
         )
 
     @Test
-    fun lockButtonIcon_whenLocked() =
-        testScope.runTest {
-            val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Password
-            )
-            utils.authenticationRepository.setUnlocked(false)
-
-            assertThat((lockButtonIcon as? Icon.Resource)?.res)
-                .isEqualTo(R.drawable.ic_device_lock_on)
-        }
-
-    @Test
-    fun lockButtonIcon_whenUnlocked() =
-        testScope.runTest {
-            val lockButtonIcon by collectLastValue(underTest.lockButtonIcon)
-            utils.authenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Password
-            )
-            utils.authenticationRepository.setUnlocked(true)
-
-            assertThat((lockButtonIcon as? Icon.Resource)?.res)
-                .isEqualTo(R.drawable.ic_device_lock_off)
-        }
-
-    @Test
     fun upTransitionSceneKey_canSwipeToUnlock_gone() =
         testScope.runTest {
             val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
@@ -120,32 +91,6 @@
         }
 
     @Test
-    fun onContentClicked_deviceUnlocked_switchesToGone() =
-        testScope.runTest {
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setUnlocked(true)
-            runCurrent()
-
-            underTest.onContentClicked()
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
-        }
-
-    @Test
-    fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
-        testScope.runTest {
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setUnlocked(false)
-            runCurrent()
-
-            underTest.onContentClicked()
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-        }
-
-    @Test
     fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 49ece66..ef07fab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.ScreenLifecycle
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.model.SysUiState
 import com.android.systemui.navigationbar.NavigationBarController
@@ -84,7 +83,6 @@
     private val fakeSystemClock = FakeSystemClock()
     private val sysUiState = SysUiState(displayTracker)
     private val featureFlags = FakeFeatureFlags()
-    private val screenLifecycle = ScreenLifecycle(dumpManager)
     private val wakefulnessLifecycle =
         WakefulnessLifecycle(mContext, null, fakeSystemClock, dumpManager)
 
@@ -142,7 +140,6 @@
                 sysUiState,
                 mock(),
                 userTracker,
-                screenLifecycle,
                 wakefulnessLifecycle,
                 uiEventLogger,
                 displayTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 53c04cc..46cbfac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -116,7 +116,6 @@
 
     private val lockscreenSceneViewModel =
         LockscreenSceneViewModel(
-            applicationScope = testScope.backgroundScope,
             authenticationInteractor = authenticationInteractor,
             bouncerInteractor = bouncerInteractor,
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
index 2b39354..d75405f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
@@ -20,6 +20,7 @@
 import android.view.MotionEvent
 import android.widget.SeekBar
 import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.settingslib.RestrictedLockUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
@@ -39,6 +40,7 @@
 import org.mockito.Mockito.isNull
 import org.mockito.Mockito.never
 import org.mockito.Mockito.notNull
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
@@ -64,6 +66,7 @@
     private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener>
     @Mock
     private lateinit var seekBar: SeekBar
+    private val uiEventLogger = UiEventLoggerFake()
     private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
 
     private lateinit var mController: BrightnessSliderController
@@ -75,7 +78,8 @@
         whenever(mirrorController.toggleSlider).thenReturn(mirror)
         whenever(motionEvent.copy()).thenReturn(motionEvent)
 
-        mController = BrightnessSliderController(brightnessSliderView, mFalsingManager)
+        mController =
+            BrightnessSliderController(brightnessSliderView, mFalsingManager, uiEventLogger)
         mController.init()
         mController.setOnChangedListener(listener)
     }
@@ -190,6 +194,7 @@
     @Test
     fun testSeekBarTrackingStarted() {
         whenever(brightnessSliderView.value).thenReturn(42)
+        val event = BrightnessSliderEvent.SLIDER_STARTED_TRACKING_TOUCH
 
         mController.onViewAttached()
         mController.setMirrorControllerAndMirror(mirrorController)
@@ -200,11 +205,14 @@
         verify(listener).onChanged(eq(true), eq(42), eq(false))
         verify(mirrorController).showMirror()
         verify(mirrorController).setLocationAndSize(brightnessSliderView)
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        assertThat(uiEventLogger.eventId(0)).isEqualTo(event.id)
     }
 
     @Test
     fun testSeekBarTrackingStopped() {
         whenever(brightnessSliderView.value).thenReturn(23)
+        val event = BrightnessSliderEvent.SLIDER_STOPPED_TRACKING_TOUCH
 
         mController.onViewAttached()
         mController.setMirrorControllerAndMirror(mirrorController)
@@ -214,5 +222,7 @@
 
         verify(listener).onChanged(eq(false), eq(23), eq(true))
         verify(mirrorController).hideMirror()
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        assertThat(uiEventLogger.eventId(0)).isEqualTo(event.id)
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index ecaf137..48665fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -340,9 +340,9 @@
     @Test
     fun knownPluginAttached_clockAndListChanged_notLoaded() {
         val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
-        whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.falcon.one")
+        whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.clocks.metro")
         val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
-        whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.falcon.two")
+        whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.clocks.bignum")
 
         var changeCallCount = 0
         var listChangeCallCount = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
index 9393a4f..ee3d870 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
@@ -60,18 +60,4 @@
 
     assertThat(mController.canShowRotationButton()).isTrue()
   }
-
-  @Test
-  fun ifTaskbarVisible_showRotationSuggestion() {
-    mController.onNavigationBarWindowVisibilityChange( /* showing = */ false)
-    mController.onBehaviorChanged(Display.DEFAULT_DISPLAY,
-                                    WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
-    mController.onNavigationModeChanged(WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON)
-    mController.onTaskbarStateChange( /* visible = */ false, /* stashed = */ false)
-    assertThat(mController.canShowRotationButton()).isFalse()
-
-    mController.onTaskbarStateChange( /* visible = */ true, /* stashed = */ false)
-
-    assertThat(mController.canShowRotationButton()).isTrue()
-  }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index 38a8f414..4a94dc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -21,21 +21,11 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
-import com.android.systemui.statusbar.notification.collection.ListEntry
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
-import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager.OnGroupExpansionChangeListener
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
 import org.junit.Assert
 import org.junit.Before
 import org.junit.Test
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
@@ -46,43 +36,13 @@
     private val groupMembershipManager: GroupMembershipManager = mock()
     private val featureFlags = FakeFeatureFlags()
 
-    private val pipeline: NotifPipeline = mock()
-    private lateinit var beforeRenderListListener: OnBeforeRenderListListener
-
-    private val summary1 = notificationEntry("foo", 1)
-    private val summary2 = notificationEntry("bar", 1)
-    private val entries =
-        listOf<ListEntry>(
-            GroupEntryBuilder()
-                .setSummary(summary1)
-                .setChildren(
-                    listOf(
-                        notificationEntry("foo", 2),
-                        notificationEntry("foo", 3),
-                        notificationEntry("foo", 4)
-                    )
-                )
-                .build(),
-            GroupEntryBuilder()
-                .setSummary(summary2)
-                .setChildren(
-                    listOf(
-                        notificationEntry("bar", 2),
-                        notificationEntry("bar", 3),
-                        notificationEntry("bar", 4)
-                    )
-                )
-                .build(),
-            notificationEntry("baz", 1)
-        )
-
-    private fun notificationEntry(pkg: String, id: Int) =
-        NotificationEntryBuilder().setPkg(pkg).setId(id).build().apply { row = mock() }
+    private val entry1 = NotificationEntryBuilder().build()
+    private val entry2 = NotificationEntryBuilder().build()
 
     @Before
     fun setUp() {
-        whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1)
-        whenever(groupMembershipManager.getGroupSummary(summary2)).thenReturn(summary2)
+        whenever(groupMembershipManager.getGroupSummary(entry1)).thenReturn(entry1)
+        whenever(groupMembershipManager.getGroupSummary(entry2)).thenReturn(entry2)
 
         gem = GroupExpansionManagerImpl(dumpManager, groupMembershipManager, featureFlags)
     }
@@ -94,15 +54,15 @@
         var listenerCalledCount = 0
         gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
 
-        gem.setGroupExpanded(summary1, false)
+        gem.setGroupExpanded(entry1, false)
         Assert.assertEquals(0, listenerCalledCount)
-        gem.setGroupExpanded(summary1, true)
+        gem.setGroupExpanded(entry1, true)
         Assert.assertEquals(1, listenerCalledCount)
-        gem.setGroupExpanded(summary2, true)
+        gem.setGroupExpanded(entry2, true)
         Assert.assertEquals(2, listenerCalledCount)
-        gem.setGroupExpanded(summary1, true)
+        gem.setGroupExpanded(entry1, true)
         Assert.assertEquals(2, listenerCalledCount)
-        gem.setGroupExpanded(summary2, false)
+        gem.setGroupExpanded(entry2, false)
         Assert.assertEquals(3, listenerCalledCount)
     }
 
@@ -113,39 +73,15 @@
         var listenerCalledCount = 0
         gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
 
-        gem.setGroupExpanded(summary1, false)
+        gem.setGroupExpanded(entry1, false)
         Assert.assertEquals(1, listenerCalledCount)
-        gem.setGroupExpanded(summary1, true)
+        gem.setGroupExpanded(entry1, true)
         Assert.assertEquals(2, listenerCalledCount)
-        gem.setGroupExpanded(summary2, true)
+        gem.setGroupExpanded(entry2, true)
         Assert.assertEquals(3, listenerCalledCount)
-        gem.setGroupExpanded(summary1, true)
+        gem.setGroupExpanded(entry1, true)
         Assert.assertEquals(4, listenerCalledCount)
-        gem.setGroupExpanded(summary2, false)
+        gem.setGroupExpanded(entry2, false)
         Assert.assertEquals(5, listenerCalledCount)
     }
-
-    @Test
-    fun testSyncWithPipeline() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-        gem.attach(pipeline)
-        beforeRenderListListener = withArgCaptor {
-            verify(pipeline).addOnBeforeRenderListListener(capture())
-        }
-
-        val listener: OnGroupExpansionChangeListener = mock()
-        gem.registerGroupExpansionChangeListener(listener)
-
-        beforeRenderListListener.onBeforeRenderList(entries)
-        verify(listener, never()).onGroupExpansionChange(any(), any())
-
-        // Expand one of the groups.
-        gem.setGroupExpanded(summary1, true)
-        verify(listener).onGroupExpansionChange(summary1.row, true)
-
-        // Empty the pipeline list and verify that the group is no longer expanded.
-        beforeRenderListListener.onBeforeRenderList(emptyList())
-        verify(listener).onGroupExpansionChange(summary1.row, false)
-        verifyNoMoreInteractions(listener)
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
index d5612e8..3f7fc97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt
@@ -20,19 +20,21 @@
 import android.testing.TestableLooper.RunWithLooper
 import android.util.AttributeSet
 import android.view.View
+import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.LinearLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertNotNull
-import junit.framework.Assert.assertNull
-import org.junit.Before
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
 
 /** Tests for [NotifLayoutInflaterFactory] */
 @SmallTest
@@ -40,87 +42,106 @@
 @RunWithLooper
 class NotifLayoutInflaterFactoryTest : SysuiTestCase() {
 
-    @Mock private lateinit var attrs: AttributeSet
+    private lateinit var inflaterFactory: NotifLayoutInflaterFactory
 
-    @Before
-    fun before() {
-        MockitoAnnotations.initMocks(this)
+    private val attrs: AttributeSet = mock()
+    private val row: ExpandableNotificationRow = mock()
+    private val textViewExpandedFactory =
+        createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_EXPANDED) { context, _ ->
+            Button(context)
+        }
+    private val textViewCollapsedFactory =
+        createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_CONTRACTED) { context, _ ->
+            Button(context)
+        }
+    private val textViewExpandedFactorySpy = spy(textViewExpandedFactory)
+    private val textViewCollapsedFactorySpy = spy(textViewCollapsedFactory)
+    private val viewFactorySpies = setOf(textViewExpandedFactorySpy, textViewCollapsedFactorySpy)
+
+    @Test
+    fun onCreateView_noMatchingViewForName_returnNull() {
+        // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+        val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+        inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
+
+        // WHEN we try to inflate an ImageView for the expanded layout
+        val createdView = inflaterFactory.onCreateView("ImageView", context, attrs)
+
+        // THEN the inflater factory returns null
+        viewFactorySpies.forEach { viewFactory ->
+            verify(viewFactory).instantiate(row, layoutType, null, "ImageView", context, attrs)
+        }
+        assertThat(createdView).isNull()
     }
 
     @Test
-    fun onCreateView_notMatchingViews_returnNull() {
-        // GIVEN
-        val layoutInflaterFactory =
-            createNotifLayoutInflaterFactoryImpl(
-                setOf(
-                    createReplacementViewFactory("TextView") { context, attrs ->
-                        FrameLayout(context)
-                    }
-                )
-            )
+    fun onCreateView_noMatchingViewForLayoutType_returnNull() {
+        // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+        val layoutType = FLAG_CONTENT_VIEW_HEADS_UP
+        inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
 
-        // WHEN
-        val createView = layoutInflaterFactory.onCreateView("ImageView", mContext, attrs)
+        // WHEN we try to inflate a TextView for the heads-up layout
+        val createdView = inflaterFactory.onCreateView("TextView", context, attrs)
 
-        // THEN
-        assertNull(createView)
+        // THEN the inflater factory returns null
+        viewFactorySpies.forEach { viewFactory ->
+            verify(viewFactory).instantiate(row, layoutType, null, "TextView", context, attrs)
+        }
+        assertThat(createdView).isNull()
     }
 
     @Test
     fun onCreateView_matchingViews_returnReplacementView() {
-        // GIVEN
-        val layoutInflaterFactory =
-            createNotifLayoutInflaterFactoryImpl(
-                setOf(
-                    createReplacementViewFactory("TextView") { context, attrs ->
-                        FrameLayout(context)
-                    }
-                )
-            )
+        // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts
+        val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+        inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies)
 
-        // WHEN
-        val createView = layoutInflaterFactory.onCreateView("TextView", mContext, attrs)
+        // WHEN we try to inflate a TextView for the expanded layout
+        val createdView = inflaterFactory.onCreateView("TextView", context, attrs)
 
-        // THEN
-        assertNotNull(createView)
-        assertEquals(requireNotNull(createView)::class.java, FrameLayout::class.java)
+        // THEN the expanded viewFactory returns the replaced view
+        verify(textViewCollapsedFactorySpy)
+            .instantiate(row, layoutType, null, "TextView", context, attrs)
+        assertThat(createdView).isInstanceOf(Button::class.java)
     }
 
     @Test(expected = IllegalStateException::class)
     fun onCreateView_multipleFactory_throwIllegalStateException() {
-        // GIVEN
-        val layoutInflaterFactory =
-            createNotifLayoutInflaterFactoryImpl(
+        // GIVEN we have two factories that replaces TextViews in expanded layouts
+        val layoutType = FLAG_CONTENT_VIEW_EXPANDED
+        inflaterFactory =
+            NotifLayoutInflaterFactory(
+                row,
+                layoutType,
                 setOf(
-                    createReplacementViewFactory("TextView") { context, attrs ->
+                    createReplacementViewFactory("TextView", layoutType) { context, _ ->
                         FrameLayout(context)
                     },
-                    createReplacementViewFactory("TextView") { context, attrs ->
+                    createReplacementViewFactory("TextView", layoutType) { context, _ ->
                         LinearLayout(context)
                     }
                 )
             )
 
-        // WHEN
-        layoutInflaterFactory.onCreateView("TextView", mContext, attrs)
+        // WHEN we try to inflate a TextView for the expanded layout
+        inflaterFactory.onCreateView("TextView", mContext, attrs)
     }
 
-    private fun createNotifLayoutInflaterFactoryImpl(
-        replacementViewFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory>
-    ) = NotifLayoutInflaterFactory(DumpManager(), replacementViewFactories)
-
     private fun createReplacementViewFactory(
         replacementName: String,
+        @InflationFlag replacementLayoutType: Int,
         createView: (context: Context, attrs: AttributeSet) -> View
     ) =
         object : NotifRemoteViewsFactory {
             override fun instantiate(
+                row: ExpandableNotificationRow,
+                @InflationFlag layoutType: Int,
                 parent: View?,
                 name: String,
                 context: Context,
                 attrs: AttributeSet
             ): View? =
-                if (replacementName == name) {
+                if (replacementName == name && replacementLayoutType == layoutType) {
                     createView(context, attrs)
                 } else {
                     null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index f55b0a8..ea87c80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -92,6 +92,7 @@
     @Mock private ConversationNotificationProcessor mConversationNotificationProcessor;
     @Mock private InflatedSmartReplyState mInflatedSmartReplyState;
     @Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies;
+    @Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
     @Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
 
     private final SmartReplyStateInflater mSmartReplyStateInflater =
@@ -124,6 +125,8 @@
                 TestableLooper.get(this));
         ExpandableNotificationRow row = helper.createRow(mBuilder.build());
         mRow = spy(row);
+        when(mNotifLayoutInflaterFactoryProvider.provide(any(), any()))
+                .thenReturn(mNotifLayoutInflaterFactory);
 
         mNotificationInflater = new NotificationContentInflater(
                 mCache,
@@ -132,7 +135,7 @@
                 mock(MediaFeatureFlag.class),
                 mock(Executor.class),
                 mSmartReplyStateInflater,
-                mNotifLayoutInflaterFactory);
+                mNotifLayoutInflaterFactoryProvider);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1ab2b38..3657bdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -175,7 +175,7 @@
                 mock(MediaFeatureFlag.class),
                 mock(Executor.class),
                 new MockSmartReplyInflater(),
-                mock(NotifLayoutInflaterFactory.class));
+                mock(NotifLayoutInflaterFactory.Provider.class));
         contentBinder.setInflateSynchronously(true);
         mBindStage = new RowContentBindStage(contentBinder,
                 mock(NotifInflationErrorManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 0c28cbb..e249cec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -259,35 +259,6 @@
         assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
     }
 
-    @Test
-    fun userSwitchingInProgress_registersUserTrackerCallback() = runSelfCancelingTest {
-        underTest = create(this)
-
-        underTest.userSwitchingInProgress.launchIn(this)
-        underTest.userSwitchingInProgress.launchIn(this)
-        underTest.userSwitchingInProgress.launchIn(this)
-
-        // Two callbacks registered - one for observing user switching and one for observing the
-        // selected user
-        assertThat(tracker.callbacks.size).isEqualTo(2)
-    }
-
-    @Test
-    fun userSwitchingInProgress_propagatesStateFromUserTracker() = runSelfCancelingTest {
-        underTest = create(this)
-        assertThat(tracker.callbacks.size).isEqualTo(2)
-
-        tracker.onUserChanging(0)
-
-        var mostRecentSwitchingValue = false
-        underTest.userSwitchingInProgress.onEach { mostRecentSwitchingValue = it }.launchIn(this)
-
-        assertThat(mostRecentSwitchingValue).isTrue()
-
-        tracker.onUserChanged(0)
-        assertThat(mostRecentSwitchingValue).isFalse()
-    }
-
     private fun createUserInfo(
         id: Int,
         isGuest: Boolean,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
index 2ef1be7..51ce9f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFacePropertyRepository.kt
@@ -17,14 +17,24 @@
 
 package com.android.systemui.biometrics.data.repository
 
-import kotlinx.coroutines.flow.Flow
+import com.android.systemui.biometrics.shared.model.LockoutMode
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 
 class FakeFacePropertyRepository : FacePropertyRepository {
     private val faceSensorInfo = MutableStateFlow<FaceSensorInfo?>(null)
-    override val sensorInfo: Flow<FaceSensorInfo?>
+    override val sensorInfo: StateFlow<FaceSensorInfo?>
         get() = faceSensorInfo
 
+    private val lockoutModesForUser = mutableMapOf<Int, LockoutMode>()
+
+    fun setLockoutMode(userId: Int, mode: LockoutMode) {
+        lockoutModesForUser[userId] = mode
+    }
+    override suspend fun getLockoutMode(userId: Int): LockoutMode {
+        return lockoutModesForUser[userId]!!
+    }
+
     fun setSensorInfo(value: FaceSensorInfo?) {
         faceSensorInfo.value = value
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index 10529e6..0847c85 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
@@ -21,7 +21,7 @@
     override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
     private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncerConstants.EXPANSION_HIDDEN)
     override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
-    private val _keyguardPosition = MutableStateFlow(0f)
+    private val _keyguardPosition = MutableStateFlow<Float?>(null)
     override val keyguardPosition = _keyguardPosition.asStateFlow()
     private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
     override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 51ee0c0..5ad19ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -56,10 +56,6 @@
         )
     override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
 
-    private val _userSwitchingInProgress = MutableStateFlow(false)
-    override val userSwitchingInProgress: Flow<Boolean>
-        get() = _userSwitchingInProgress
-
     override var mainUserId: Int = MAIN_USER_ID
     override var lastSelectedNonGuestUserId: Int = mainUserId
 
@@ -120,8 +116,4 @@
     fun setGuestUserAutoCreated(value: Boolean) {
         _isGuestUserAutoCreated = value
     }
-
-    fun setUserSwitching(value: Boolean) {
-        _userSwitchingInProgress.value = value
-    }
 }
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index a248d9e5..25f57b3 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -26,5 +26,6 @@
     ],
     static_libs: [
         "ukey2_jni",
+        "virtualdevice_flags_lib",
     ],
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
index 50a09b9..6526c78 100644
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ b/services/companion/java/com/android/server/companion/virtual/Android.bp
@@ -9,4 +9,4 @@
     srcs: [
         "flags.aconfig",
     ],
-}
\ No newline at end of file
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index d1be5a9..f23fe2a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -126,6 +126,7 @@
     private final VirtualDeviceManagerService mService;
     private final PendingTrampolineCallback mPendingTrampolineCallback;
     private final int mOwnerUid;
+    private final VirtualDeviceLog mVirtualDeviceLog;
     private final String mOwnerPackageName;
     private int mDeviceId;
     private @Nullable String mPersistentDeviceId;
@@ -197,6 +198,7 @@
             Context context,
             AssociationInfo associationInfo,
             VirtualDeviceManagerService service,
+            VirtualDeviceLog virtualDeviceLog,
             IBinder token,
             AttributionSource attributionSource,
             int deviceId,
@@ -210,6 +212,7 @@
                 context,
                 associationInfo,
                 service,
+                virtualDeviceLog,
                 token,
                 attributionSource,
                 deviceId,
@@ -228,6 +231,7 @@
             Context context,
             AssociationInfo associationInfo,
             VirtualDeviceManagerService service,
+            VirtualDeviceLog virtualDeviceLog,
             IBinder token,
             AttributionSource attributionSource,
             int deviceId,
@@ -240,6 +244,7 @@
             VirtualDeviceParams params,
             DisplayManagerGlobal displayManager) {
         super(PermissionEnforcer.fromContext(context));
+        mVirtualDeviceLog = virtualDeviceLog;
         mOwnerPackageName = attributionSource.getPackageName();
         UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
         mContext = context.createContextAsUser(ownerUserHandle, 0);
@@ -271,6 +276,7 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+        mVirtualDeviceLog.logCreated(deviceId, mOwnerUid);
     }
 
     @VisibleForTesting
@@ -405,6 +411,7 @@
         super.close_enforcePermission();
         // Remove about-to-be-closed virtual device from the service before butchering it.
         boolean removed = mService.removeVirtualDevice(mDeviceId);
+        mVirtualDeviceLog.logClosed(mDeviceId, mOwnerUid);
         mDeviceId = Context.DEVICE_ID_INVALID;
 
         // Device is already closed.
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
new file mode 100644
index 0000000..c65aa5b
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 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.companion.virtual;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayDeque;
+
+final class VirtualDeviceLog {
+    public static int TYPE_CREATED = 0x0;
+    public static int TYPE_CLOSED = 0x1;
+
+    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(
+            "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
+    private static final int MAX_ENTRIES = 16;
+
+    private final Context mContext;
+    private final ArrayDeque<LogEntry> mLogEntries = new ArrayDeque<>();
+
+    VirtualDeviceLog(Context context) {
+        mContext = context;
+    }
+
+    void logCreated(int deviceId, int ownerUid) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (!Flags.dumpHistory()) {
+                return;
+            }
+            addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid));
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    void logClosed(int deviceId, int ownerUid) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (!Flags.dumpHistory()) {
+                return;
+            }
+            addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid));
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void addEntry(LogEntry entry) {
+        mLogEntries.push(entry);
+        if (mLogEntries.size() > MAX_ENTRIES) {
+            mLogEntries.removeLast();
+        }
+    }
+
+    void dump(PrintWriter pw) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (!Flags.dumpHistory()) {
+                return;
+            }
+            pw.println("VirtualDevice Log:");
+            UidToPackageNameCache packageNameCache = new UidToPackageNameCache(
+                    mContext.getPackageManager());
+            for (LogEntry logEntry : mLogEntries) {
+                logEntry.dump(pw, "  ", packageNameCache);
+
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    static class LogEntry {
+        private final int mType;
+        private final int mDeviceId;
+        private final long mTimestamp;
+        private final int mOwnerUid;
+
+        LogEntry(int type, int deviceId, long timestamp, int ownerUid) {
+            this.mType = type;
+            this.mDeviceId = deviceId;
+            this.mTimestamp = timestamp;
+            this.mOwnerUid = ownerUid;
+        }
+
+        void dump(PrintWriter pw, String prefix, UidToPackageNameCache packageNameCache) {
+            StringBuilder sb = new StringBuilder(prefix);
+            sb.append(DATE_FORMAT.format(Instant.ofEpochMilli(mTimestamp)));
+            sb.append(" - ");
+            sb.append(mType == TYPE_CREATED ? "START" : "CLOSE");
+            sb.append(" Device ID: ");
+            sb.append(mDeviceId);
+            sb.append(" ");
+            sb.append(mOwnerUid);
+            sb.append(" (");
+            sb.append(packageNameCache.getPackageName(mOwnerUid));
+            sb.append(")");
+            pw.println(sb);
+        }
+    }
+
+    private static class UidToPackageNameCache {
+        private final PackageManager mPackageManager;
+        private final SparseArray<String> mUidToPackagesCache = new SparseArray<>();
+
+        public UidToPackageNameCache(PackageManager packageManager) {
+            mPackageManager = packageManager;
+        }
+
+        String getPackageName(int ownerUid) {
+            String[] packages;
+            if (mUidToPackagesCache.contains(ownerUid)) {
+                return mUidToPackagesCache.get(ownerUid);
+            } else {
+                packages = mPackageManager.getPackagesForUid(ownerUid);
+                String packageName = "";
+                if (packages != null && packages.length > 0) {
+                    packageName = packages[0];
+                    if (packages.length > 1) {
+                        StringBuilder sb = new StringBuilder();
+                        sb.append(packageName)
+                                .append(",...");
+                        packageName = sb.toString();
+                    }
+                }
+                mUidToPackagesCache.put(ownerUid, packageName);
+                return packageName;
+            }
+        }
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index e558498..7429fbe 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -85,6 +85,7 @@
     private final Object mVirtualDeviceManagerLock = new Object();
     private final VirtualDeviceManagerImpl mImpl;
     private final VirtualDeviceManagerInternal mLocalService;
+    private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
 
@@ -344,9 +345,11 @@
             final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
                     runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
             VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo,
-                    VirtualDeviceManagerService.this, token, attributionSource, deviceId,
+                    VirtualDeviceManagerService.this, mVirtualDeviceLog, token, attributionSource,
+                    deviceId,
                     cameraAccessController, mPendingTrampolineCallback, activityListener,
                     soundEffectListener, runningAppsChangedCallback, params);
+
             synchronized (mVirtualDeviceManagerLock) {
                 if (mVirtualDevices.size() == 0) {
                     final long callindId = Binder.clearCallingIdentity();
@@ -521,6 +524,8 @@
             for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
                 virtualDevicesSnapshot.get(i).dump(fd, fout, args);
             }
+
+            mVirtualDeviceLog.dump(fout);
         }
     }
 
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 173a75b..7907d61 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -80,9 +80,9 @@
 import android.util.apk.ApkSigningBlockUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.expresslog.Histogram;
 import com.android.internal.os.IBinaryTransparencyService;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.modules.expresslog.Histogram;
 import com.android.server.pm.ApexManager;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.AndroidPackageSplit;
@@ -1391,7 +1391,7 @@
         // Check the flag to determine whether biometric property verification is enabled. It's
         // disabled by default.
         if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BIOMETRICS,
-                KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, false)) {
+                KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION, true)) {
             if (DEBUG) {
                 Slog.d(TAG, "Do not collect/verify biometric properties. Feature disabled by "
                         + "DeviceConfig");
diff --git a/services/core/java/com/android/server/CertBlacklister.java b/services/core/java/com/android/server/CertBlacklister.java
index c16378b..e726c6a 100644
--- a/services/core/java/com/android/server/CertBlacklister.java
+++ b/services/core/java/com/android/server/CertBlacklister.java
@@ -31,17 +31,17 @@
 import libcore.io.IoUtils;
 
 /**
- * <p>CertBlacklister provides a simple mechanism for updating the platform blacklists for SSL
+ * <p>CertBlacklister provides a simple mechanism for updating the platform denylists for SSL
  * certificate public keys and serial numbers.
  */
 public class CertBlacklister extends Binder {
 
     private static final String TAG = "CertBlacklister";
 
-    private static final String BLACKLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
+    private static final String DENYLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
 
-    public static final String PUBKEY_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt";
-    public static final String SERIAL_PATH = BLACKLIST_ROOT + "serial_blacklist.txt";
+    public static final String PUBKEY_PATH = DENYLIST_ROOT + "pubkey_blacklist.txt";
+    public static final String SERIAL_PATH = DENYLIST_ROOT + "serial_blacklist.txt";
 
     public static final String PUBKEY_BLACKLIST_KEY = "pubkey_blacklist";
     public static final String SERIAL_BLACKLIST_KEY = "serial_blacklist";
@@ -66,14 +66,14 @@
         @Override
         public void onChange(boolean selfChange) {
             super.onChange(selfChange);
-            writeBlacklist();
+            writeDenylist();
         }
 
         public String getValue() {
             return Settings.Secure.getString(mContentResolver, mKey);
         }
 
-        private void writeBlacklist() {
+        private void writeDenylist() {
             new Thread("BlacklistUpdater") {
                 public void run() {
                     synchronized(mTmpDir) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7fb9580..4d249ab 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -73,8 +73,8 @@
 import android.content.pm.UserInfo;
 import android.content.res.ObbInfo;
 import android.database.ContentObserver;
-import android.media.MediaCodecList;
 import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
 import android.media.MediaFormat;
 import android.net.Uri;
 import android.os.BatteryManager;
@@ -219,6 +219,8 @@
     @GuardedBy("mLock")
     private final Set<Integer> mCeStoragePreparedUsers = new ArraySet<>();
 
+    private volatile long mInternalStorageSize = 0;
+
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
 
@@ -3519,6 +3521,15 @@
         return authority;
     }
 
+    @Override
+    public long getInternalStorageBlockDeviceSize() throws RemoteException {
+        if (mInternalStorageSize == 0) {
+            mInternalStorageSize = mVold.getStorageSize();
+        }
+
+        return mInternalStorageSize;
+    }
+
     /**
      * Enforces that the caller is the {@link ExternalStorageService}
      *
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d3bfc9a..942d35a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2514,30 +2514,36 @@
         StopUserCallback callback = wait ? new StopUserCallback(userId) : null;
 
         Slogf.d(TAG, "Calling stopUser(%d, %b, %s)", userId, force, callback);
-        int res = mInterface.stopUser(userId, force, callback);
-        if (res != ActivityManager.USER_OP_SUCCESS) {
-            String txt = "";
-            switch (res) {
-                case ActivityManager.USER_OP_IS_CURRENT:
-                    txt = " (Can't stop current user)";
-                    break;
-                case ActivityManager.USER_OP_UNKNOWN_USER:
-                    txt = " (Unknown user " + userId + ")";
-                    break;
-                case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
-                    txt = " (System user cannot be stopped)";
-                    break;
-                case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
-                    txt = " (Can't stop user " + userId
-                            + " - one of its related users can't be stopped)";
-                    break;
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "shell_runStopUser-" + userId + "-[stopUser]");
+        try {
+            int res = mInterface.stopUser(userId, force, callback);
+            if (res != ActivityManager.USER_OP_SUCCESS) {
+                String txt = "";
+                switch (res) {
+                    case ActivityManager.USER_OP_IS_CURRENT:
+                        txt = " (Can't stop current user)";
+                        break;
+                    case ActivityManager.USER_OP_UNKNOWN_USER:
+                        txt = " (Unknown user " + userId + ")";
+                        break;
+                    case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
+                        txt = " (System user cannot be stopped)";
+                        break;
+                    case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
+                        txt = " (Can't stop user " + userId
+                                + " - one of its related users can't be stopped)";
+                        break;
+                }
+                getErrPrintWriter().println("Switch failed: " + res + txt);
+                return -1;
+            } else if (callback != null) {
+                callback.waitForFinish();
             }
-            getErrPrintWriter().println("Switch failed: " + res + txt);
-            return -1;
-        } else if (callback != null) {
-            callback.waitForFinish();
+            return 0;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
-        return 0;
     }
 
     int runIsUserStopped(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d426c79..2770833 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -105,6 +105,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
@@ -921,13 +922,24 @@
 
     int stopUser(final int userId, final boolean force, boolean allowDelayedLocking,
             final IStopUserCallback stopUserCallback, KeyEvictedCallback keyEvictedCallback) {
-        checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
-        Preconditions.checkArgument(userId >= 0, "Invalid user id %d", userId);
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
 
-        enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
-        synchronized (mLock) {
-            return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
-                    keyEvictedCallback);
+        t.traceBegin("UserController"
+                + (force ? "-force" : "")
+                + (allowDelayedLocking ? "-allowDelayedLocking" : "")
+                + (stopUserCallback != null ? "-withStopUserCallback" : "")
+                + "-" + userId + "-[stopUser]");
+        try {
+            checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "stopUser");
+            Preconditions.checkArgument(userId >= 0, "Invalid user id %d", userId);
+
+            enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId);
+            synchronized (mLock) {
+                return stopUsersLU(userId, force, allowDelayedLocking, stopUserCallback,
+                        keyEvictedCallback);
+            }
+        } finally {
+            t.traceEnd();
         }
     }
 
@@ -944,10 +956,10 @@
         if (isCurrentUserLU(userId)) {
             return USER_OP_IS_CURRENT;
         }
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         int[] usersToStop = getUsersToStopLU(userId);
         // If one of related users is system or current, no related users should be stopped
-        for (int i = 0; i < usersToStop.length; i++) {
-            int relatedUserId = usersToStop[i];
+        for (int relatedUserId : usersToStop) {
             if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
                 if (DEBUG_MU) {
                     Slogf.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId);
@@ -956,8 +968,10 @@
                 if (force) {
                     Slogf.i(TAG,
                             "Force stop user " + userId + ". Related users will not be stopped");
+                    t.traceBegin("stopSingleUserLU-force-" + userId + "-[stopUser]");
                     stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
                             keyEvictedCallback);
+                    t.traceEnd();
                     return USER_OP_SUCCESS;
                 }
                 return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
@@ -965,9 +979,11 @@
         }
         if (DEBUG_MU) Slogf.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
         for (int userIdToStop : usersToStop) {
+            t.traceBegin("stopSingleUserLU-" + userIdToStop + "-[stopUser]");
             stopSingleUserLU(userIdToStop, allowDelayedLocking,
                     userIdToStop == userId ? stopUserCallback : null,
                     userIdToStop == userId ? keyEvictedCallback : null);
+            t.traceEnd();
         }
         return USER_OP_SUCCESS;
     }
@@ -1047,14 +1063,24 @@
                 && uss.state != UserState.STATE_SHUTDOWN) {
             uss.setState(UserState.STATE_STOPPING);
             UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
+            TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+            t.traceBegin("setUserState-STATE_STOPPING-" + userId + "-[stopUser]");
             userManagerInternal.setUserState(userId, uss.state);
+            t.traceEnd();
+            t.traceBegin("unassignUserFromDisplayOnStop-" + userId + "-[stopUser]");
             userManagerInternal.unassignUserFromDisplayOnStop(userId);
+            t.traceEnd();
 
             updateStartedUserArrayLU();
 
             final boolean allowDelayedLockingCopied = allowDelayedLocking;
             Runnable finishUserStoppingAsync = () ->
-                    mHandler.post(() -> finishUserStopping(userId, uss, allowDelayedLockingCopied));
+                    mHandler.post(() -> {
+                        TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+                        t2.traceBegin("finishUserStopping-" + userId + "-[stopUser]");
+                        finishUserStopping(userId, uss, allowDelayedLockingCopied);
+                        t2.traceEnd();
+                    });
 
             if (mInjector.getUserManager().isPreCreated(userId)) {
                 finishUserStoppingAsync.run();
@@ -1075,12 +1101,18 @@
                     @Override
                     public void performReceive(Intent intent, int resultCode, String data,
                             Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+                        asyncTraceEnd("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]",
+                                userId);
                         finishUserStoppingAsync.run();
                     }
                 };
 
+                TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+                t2.traceBegin("clearBroadcastQueueForUser-" + userId + "-[stopUser]");
                 // Clear broadcast queue for the user to avoid delivering stale broadcasts
                 mInjector.clearBroadcastQueueForUser(userId);
+                t2.traceEnd();
+                asyncTraceBegin("broadcast-ACTION_USER_STOPPING-" + userId + "-[stopUser]", userId);
                 // Kick things off.
                 mInjector.broadcastIntent(stoppingIntent,
                         null, stoppingReceiver, 0, null, null,
@@ -1111,7 +1143,10 @@
             }
             uss.setState(UserState.STATE_SHUTDOWN);
         }
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("setUserState-STATE_SHUTDOWN-" + userId + "-[stopUser]");
         mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+        t.traceEnd();
 
         mInjector.batteryStatsServiceNoteEvent(
                 BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
@@ -1119,7 +1154,12 @@
         mInjector.getSystemServiceManager().onUserStopping(userId);
 
         Runnable finishUserStoppedAsync = () ->
-                mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking));
+                mHandler.post(() -> {
+                    TimingsTraceAndSlog t2 = new TimingsTraceAndSlog();
+                    t2.traceBegin("finishUserStopped-" + userId + "-[stopUser]");
+                    finishUserStopped(uss, allowDelayedLocking);
+                    t2.traceEnd();
+                });
         if (mInjector.getUserManager().isPreCreated(userId)) {
             finishUserStoppedAsync.run();
             return;
@@ -1132,9 +1172,11 @@
             @Override
             public void performReceive(Intent intent, int resultCode, String data,
                     Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+                asyncTraceEnd("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
                 finishUserStoppedAsync.run();
             }
         };
+        asyncTraceBegin("broadcast-ACTION_SHUTDOWN-" + userId + "-[stopUser]", userId);
         mInjector.broadcastIntent(shutdownIntent,
                 null, shutdownReceiver, 0, null, null, null,
                 AppOpsManager.OP_NONE,
@@ -1185,6 +1227,7 @@
                 }
             }
         }
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         if (stopped) {
             Slogf.i(TAG, "Removing user state from UserManager.mUserStates for user #" + userId
                     + " as a result of user being stopped");
@@ -1193,20 +1236,33 @@
             mInjector.activityManagerOnUserStopped(userId);
             // Clean up all state and processes associated with the user.
             // Kill all the processes for the user.
+            t.traceBegin("forceStopUser-" + userId + "-[stopUser]");
             forceStopUser(userId, "finish user");
+            t.traceEnd();
         }
 
         for (final IStopUserCallback callback : stopCallbacks) {
             try {
-                if (stopped) callback.userStopped(userId);
-                else callback.userStopAborted(userId);
+                if (stopped) {
+                    t.traceBegin("stopCallbacks.userStopped-" + userId + "-[stopUser]");
+                    callback.userStopped(userId);
+                    t.traceEnd();
+                } else {
+                    t.traceBegin("stopCallbacks.userStopAborted-" + userId + "-[stopUser]");
+                    callback.userStopAborted(userId);
+                    t.traceEnd();
+                }
             } catch (RemoteException ignored) {
             }
         }
 
         if (stopped) {
+            t.traceBegin("systemServiceManagerOnUserStopped-" + userId + "-[stopUser]");
             mInjector.systemServiceManagerOnUserStopped(userId);
+            t.traceEnd();
+            t.traceBegin("taskSupervisorRemoveUser-" + userId + "-[stopUser]");
             mInjector.taskSupervisorRemoveUser(userId);
+            t.traceEnd();
 
             // Remove the user if it is ephemeral.
             if (userInfo.isEphemeral() && !userInfo.preCreated) {
@@ -3361,6 +3417,16 @@
         return DEFAULT_USER_SWITCH_TIMEOUT_MS;
     }
 
+    private static void asyncTraceBegin(String msg, int cookie) {
+        Slogf.d(TAG, "%s - asyncTraceBegin(%d)", msg, cookie);
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
+    }
+
+    private static void asyncTraceEnd(String msg, int cookie) {
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg, cookie);
+        Slogf.d(TAG, "%s - asyncTraceEnd(%d)", msg, cookie);
+    }
+
     /**
      * Uptime when any user was being unlocked most recently. 0 if no users have been unlocked
      * yet. To avoid lock contention (since it's used by OomAdjuster), it's volatile internally.
@@ -3475,12 +3541,16 @@
                 ordered = false;
             }
 
+            TimingsTraceAndSlog t = new TimingsTraceAndSlog();
             // TODO b/64165549 Verify that mLock is not held before calling AMS methods
             synchronized (mService) {
-                return mService.broadcastIntentLocked(null, null, null, intent, resolvedType,
-                        resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
-                        null, appOp, bOptions, ordered, sticky, callingPid, callingUid,
-                        realCallingUid, realCallingPid, userId);
+                t.traceBegin("broadcastIntent-" + userId + "-" + intent.getAction());
+                final int result = mService.broadcastIntentLocked(null, null, null, intent,
+                        resolvedType, resultTo, resultCode, resultData, resultExtras,
+                        requiredPermissions, null, null, appOp, bOptions, ordered, sticky,
+                        callingPid, callingUid, realCallingUid, realCallingPid, userId);
+                t.traceEnd();
+                return result;
             }
         }
 
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index 247094f..ba43c8d 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -221,7 +221,7 @@
             }
             final AdiDeviceState deviceState = new AdiDeviceState(deviceType,
                     internalDeviceType, fields[1]);
-            deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1);
+            deviceState.setSAEnabled(Integer.parseInt(fields[2]) == 1);
             deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
             deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
             deviceState.setAudioDeviceCategory(audioDeviceCategory);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 4a523af..d0d5cda 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2273,7 +2273,7 @@
         if (btSCoOn) {
             // Use the SCO device known to BtHelper so that it matches exactly
             // what has been communicated to audio policy manager. The device
-            // returned by requestedCommunicationDevice() can be a dummy SCO device if legacy
+            // returned by requestedCommunicationDevice() can be a placeholder SCO device if legacy
             // APIs are used to start SCO audio.
             AudioDeviceAttributes device = mBtHelper.getHeadsetAudioDevice();
             if (device != null) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 97e5c6f..54b34de 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -26,6 +26,7 @@
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.UserHandle;
+import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -48,12 +49,14 @@
     // Upload the data every 50 attempts (average number of daily authentications).
     private static final int AUTHENTICATION_UPLOAD_INTERVAL = 50;
     // The maximum number of eligible biometric enrollment notification can be sent.
-    private static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
+    @VisibleForTesting
+    static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
 
     @NonNull private final Context mContext;
 
     private final float mThreshold;
     private final int mModality;
+    private boolean mPersisterInitialized = false;
 
     @NonNull private final Map<Integer, AuthenticationStats> mUserAuthenticationStatsMap;
 
@@ -85,9 +88,15 @@
     }
 
     private void initializeUserAuthenticationStatsMap() {
-        mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext);
-        for (AuthenticationStats stats : mAuthenticationStatsPersister.getAllFrrStats(mModality)) {
-            mUserAuthenticationStatsMap.put(stats.getUserId(), stats);
+        try {
+            mAuthenticationStatsPersister = new AuthenticationStatsPersister(mContext);
+            for (AuthenticationStats stats :
+                    mAuthenticationStatsPersister.getAllFrrStats(mModality)) {
+                mUserAuthenticationStatsMap.put(stats.getUserId(), stats);
+            }
+            mPersisterInitialized = true;
+        } catch (IllegalStateException e) {
+            Slog.w(TAG, "Failed to initialize AuthenticationStatsPersister.", e);
         }
     }
 
@@ -106,9 +115,15 @@
 
         AuthenticationStats authenticationStats = mUserAuthenticationStatsMap.get(userId);
 
+        if (authenticationStats.getEnrollmentNotifications() >= MAXIMUM_ENROLLMENT_NOTIFICATIONS) {
+            return;
+        }
+
         authenticationStats.authenticate(authenticated);
 
-        persistDataIfNeeded(userId);
+        if (mPersisterInitialized) {
+            persistDataIfNeeded(userId);
+        }
         sendNotificationIfNeeded(userId);
     }
 
@@ -166,11 +181,13 @@
     }
 
     private void onUserRemoved(final int userId) {
-        if (mAuthenticationStatsPersister == null) {
+        if (!mPersisterInitialized) {
             initializeUserAuthenticationStatsMap();
         }
-        mUserAuthenticationStatsMap.remove(userId);
-        mAuthenticationStatsPersister.removeFrrStats(userId);
+        if (mPersisterInitialized) {
+            mUserAuthenticationStatsMap.remove(userId);
+            mAuthenticationStatsPersister.removeFrrStats(userId);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
index 21e93a8..74e1410 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
@@ -21,6 +21,7 @@
 import android.content.SharedPreferences;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.Environment;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import org.json.JSONException;
@@ -72,14 +73,16 @@
                 JSONObject frrStatsJson = new JSONObject(frrStats);
                 if (modality == BiometricsProtoEnums.MODALITY_FACE) {
                     authenticationStatsList.add(new AuthenticationStats(
-                            getIntValue(frrStatsJson, USER_ID, -1 /* defaultValue */),
+                            getIntValue(frrStatsJson, USER_ID,
+                                    UserHandle.USER_NULL /* defaultValue */),
                             getIntValue(frrStatsJson, FACE_ATTEMPTS),
                             getIntValue(frrStatsJson, FACE_REJECTIONS),
                             getIntValue(frrStatsJson, ENROLLMENT_NOTIFICATIONS),
                             modality));
                 } else if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) {
                     authenticationStatsList.add(new AuthenticationStats(
-                            getIntValue(frrStatsJson, USER_ID, -1 /* defaultValue */),
+                            getIntValue(frrStatsJson, USER_ID,
+                                    UserHandle.USER_NULL /* defaultValue */),
                             getIntValue(frrStatsJson, FINGERPRINT_ATTEMPTS),
                             getIntValue(frrStatsJson, FINGERPRINT_REJECTIONS),
                             getIntValue(frrStatsJson, ENROLLMENT_NOTIFICATIONS),
@@ -138,13 +141,11 @@
 
             // If there's existing frr stats in the file, we want to update the stats for the given
             // modality and keep the stats for other modalities.
-            if (frrStatJson != null) {
-                frrStatsSet.add(buildFrrStats(frrStatJson, totalAttempts, rejectedAttempts,
-                        enrollmentNotifications, modality));
-            } else {
-                frrStatsSet.add(buildFrrStats(userId, totalAttempts, rejectedAttempts,
-                        enrollmentNotifications, modality));
+            if (frrStatJson == null) {
+                frrStatJson = new JSONObject().put(USER_ID, userId);
             }
+            frrStatsSet.add(buildFrrStats(frrStatJson, totalAttempts, rejectedAttempts,
+                    enrollmentNotifications, modality));
 
             mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply();
 
@@ -177,29 +178,6 @@
         }
     }
 
-    // Build string for new user and new authentication stats.
-    private String buildFrrStats(int userId, int totalAttempts, int rejectedAttempts,
-            int enrollmentNotifications, int modality)
-            throws JSONException {
-        if (modality == BiometricsProtoEnums.MODALITY_FACE) {
-            return new JSONObject()
-                    .put(USER_ID, userId)
-                    .put(FACE_ATTEMPTS, totalAttempts)
-                    .put(FACE_REJECTIONS, rejectedAttempts)
-                    .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications)
-                    .toString();
-        } else if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) {
-            return new JSONObject()
-                    .put(USER_ID, userId)
-                    .put(FINGERPRINT_ATTEMPTS, totalAttempts)
-                    .put(FINGERPRINT_REJECTIONS, rejectedAttempts)
-                    .put(ENROLLMENT_NOTIFICATIONS, enrollmentNotifications)
-                    .toString();
-        } else {
-            return "";
-        }
-    }
-
     private String getValue(JSONObject jsonObject, String key) throws JSONException {
         return jsonObject.has(key) ? jsonObject.getString(key) : "";
     }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 0aac7c2..e072eeb 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -783,7 +783,7 @@
         // This can be noisy, therefore we will allowlist sync adapters installed
         // before we started checking for account access because they already know
         // the account (they run before) which is the genie is out of the bottle.
-        whiteListExistingSyncAdaptersIfNeeded();
+        allowListExistingSyncAdaptersIfNeeded();
 
         mLogger.log("Sync manager initialized: " + Build.FINGERPRINT);
     }
@@ -829,7 +829,7 @@
         }
     }
 
-    private void whiteListExistingSyncAdaptersIfNeeded() {
+    private void allowListExistingSyncAdaptersIfNeeded() {
         if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) {
             return;
         }
diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java
index 2a5858c..409b469 100644
--- a/services/core/java/com/android/server/content/SyncManagerConstants.java
+++ b/services/core/java/com/android/server/content/SyncManagerConstants.java
@@ -52,11 +52,11 @@
     private static final int DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION = 5;
     private int mMaxRetriesWithAppStandbyExemption = DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION;
 
-    private static final String KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS =
+    private static final String KEY_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS =
             "exemption_temp_whitelist_duration_in_seconds";
-    private static final int DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS = 10 * 60;
+    private static final int DEF_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS = 10 * 60;
     private int mKeyExemptionTempWhitelistDurationInSeconds
-            = DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS;
+            = DEF_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS;
 
     protected SyncManagerConstants(Context context) {
         super(null);
@@ -105,8 +105,8 @@
                     DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION);
 
             mKeyExemptionTempWhitelistDurationInSeconds = parser.getInt(
-                    KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS,
-                    DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS);
+                    KEY_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS,
+                    DEF_EXEMPTION_TEMP_ALLOWLIST_DURATION_IN_SECONDS);
 
         }
     }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index d647757..80c3a27 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -119,6 +119,8 @@
     // hysteresis threshold.
     private final long mBrighteningLightDebounceConfig;
     private final long mDarkeningLightDebounceConfig;
+    private final long mBrighteningLightDebounceConfigIdle;
+    private final long mDarkeningLightDebounceConfigIdle;
 
     // If true immediately after the screen is turned on the controller will try to adjust the
     // brightness based on the current sensor reads. If false, the controller will collect more data
@@ -253,6 +255,7 @@
             int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+            long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds,
             HysteresisLevels ambientBrightnessThresholdsIdle,
@@ -265,7 +268,8 @@
                 interactiveModeBrightnessMapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
-                darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
+                darkeningLightDebounceConfig, brighteningLightDebounceConfigIdle,
+                darkeningLightDebounceConfigIdle, resetAmbientLuxAfterWarmUpConfig,
                 ambientBrightnessThresholds, screenBrightnessThresholds,
                 ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context,
                 brightnessModeController, brightnessThrottler, idleModeBrightnessMapper,
@@ -280,6 +284,7 @@
             int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+            long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds,
             HysteresisLevels ambientBrightnessThresholdsIdle,
@@ -303,6 +308,8 @@
         mCurrentLightSensorRate = -1;
         mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
         mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
+        mBrighteningLightDebounceConfigIdle = brighteningLightDebounceConfigIdle;
+        mDarkeningLightDebounceConfigIdle = darkeningLightDebounceConfigIdle;
         mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
         mAmbientLightHorizonLong = ambientLightHorizonLong;
         mAmbientLightHorizonShort = ambientLightHorizonShort;
@@ -366,9 +373,10 @@
     }
 
     /**
-     * @return The current brightness recommendation calculated from the current conditions.
-     * @param brightnessEvent Event object to populate with details about why the specific
-     *                        brightness was chosen.
+     * @param brightnessEvent Holds details about how the brightness is calculated.
+     *
+     * @return The current automatic brightness recommended value. Populates brightnessEvent
+     *         parameters with details about how the brightness was calculated.
      */
     public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
         if (brightnessEvent != null) {
@@ -560,6 +568,8 @@
         pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
         pw.println("  mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
         pw.println("  mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
+        pw.println("  mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
+        pw.println("  mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
         pw.println("  mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
         pw.println("  mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
         pw.println("  mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
@@ -820,7 +830,8 @@
             }
             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
         }
-        return earliestValidTime + mBrighteningLightDebounceConfig;
+        return earliestValidTime + (isInIdleMode()
+                ? mBrighteningLightDebounceConfigIdle : mBrighteningLightDebounceConfig);
     }
 
     private long nextAmbientLightDarkeningTransition(long time) {
@@ -832,7 +843,8 @@
             }
             earliestValidTime = mAmbientLightRingBuffer.getTime(i);
         }
-        return earliestValidTime + mDarkeningLightDebounceConfig;
+        return earliestValidTime + (isInIdleMode()
+                ? mDarkeningLightDebounceConfigIdle : mDarkeningLightDebounceConfig);
     }
 
     private void updateAmbientLux() {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 46cd496..0d6635d 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -40,6 +40,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.window.ScreenCapture;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -138,8 +139,13 @@
     public static final int MODE_FADE = 2;
 
     public ColorFade(int displayId) {
+        this(displayId, LocalServices.getService(DisplayManagerInternal.class));
+    }
+
+    @VisibleForTesting
+    ColorFade(int displayId, DisplayManagerInternal displayManagerInternal) {
         mDisplayId = displayId;
-        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+        mDisplayManagerInternal = displayManagerInternal;
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 01eceda..c0c60a4 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -184,6 +184,7 @@
  *        <defaultRefreshRateInHbmSunlight>75</defaultRefreshRateInHbmSunlight>
  *        <lowerBlockingZoneConfigs>
  *          <defaultRefreshRate>75</defaultRefreshRate>
+ *          <refreshRateThermalThrottlingId>id_of_a_throttling_map</refreshRateThermalThrottlingId>
  *          <blockingZoneThreshold>
  *            <displayBrightnessPoint>
  *              <lux>50</lux>
@@ -252,13 +253,19 @@
  *       <quirk>canSetBrightnessViaHwc</quirk>
  *      </quirks>
  *
- *      <autoBrightness enable="true">
+ *      <autoBrightness enabled="true">
  *          <brighteningLightDebounceMillis>
  *              2000
  *          </brighteningLightDebounceMillis>
  *          <darkeningLightDebounceMillis>
- *              1000
+ *              4000
  *          </darkeningLightDebounceMillis>
+ *          <brighteningLightDebounceIdleMillis>
+ *              2000
+ *          </brighteningLightDebounceIdleMillis>
+ *          <darkeningLightDebounceIdleMillis>
+ *              1000
+ *          </darkeningLightDebounceIdleMillis>
  *          <displayBrightnessMapping>
  *              <displayBrightnessPoint>
  *                  <lux>50</lux>
@@ -649,6 +656,14 @@
     private long mAutoBrightnessDarkeningLightDebounce =
             INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
 
+    // Represents the auto-brightness brightening light debounce for idle screen brightness mode.
+    private long mAutoBrightnessBrighteningLightDebounceIdle =
+            INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
+    // Represents the auto-brightness darkening light debounce for idle screen brightness mode.
+    private long mAutoBrightnessDarkeningLightDebounceIdle =
+            INVALID_AUTO_BRIGHTNESS_LIGHT_DEBOUNCE;
+
     // This setting allows non-default displays to have autobrightness enabled.
     private boolean mAutoBrightnessAvailable = false;
     // This stores the raw value loaded from the config file - true if not written.
@@ -732,6 +747,12 @@
     private float[] mHighDisplayBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
     private float[] mHighAmbientBrightnessThresholds = DEFAULT_BRIGHTNESS_THRESHOLDS;
 
+    /**
+     * Thermal throttling maps for the low and high blocking zones.
+     */
+    private String mLowBlockingZoneThermalMapId = null;
+    private String mHighBlockingZoneThermalMapId = null;
+
     private final HashMap<String, ThermalBrightnessThrottlingData>
             mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
 
@@ -1440,6 +1461,20 @@
     }
 
     /**
+     * @return Auto brightness darkening light debounce for idle screen brightness mode
+     */
+    public long getAutoBrightnessDarkeningLightDebounceIdle() {
+        return mAutoBrightnessDarkeningLightDebounceIdle;
+    }
+
+    /**
+     * @return Auto brightness brightening light debounce for idle screen brightness mode
+     */
+    public long getAutoBrightnessBrighteningLightDebounceIdle() {
+        return mAutoBrightnessBrighteningLightDebounceIdle;
+    }
+
+    /**
      * @return Auto brightness brightening ambient lux levels
      */
     public float[] getAutoBrightnessBrighteningLevelsLux() {
@@ -1536,6 +1571,13 @@
     }
 
     /**
+     * @return The refresh rate thermal map for low blocking zone.
+     */
+    public SparseArray<SurfaceControl.RefreshRateRange> getLowBlockingZoneThermalMap() {
+        return getThermalRefreshRateThrottlingData(mLowBlockingZoneThermalMapId);
+    }
+
+    /**
      * @return An array of high display brightness thresholds. This, in combination with high
      * ambient brightness thresholds help define buckets in which the refresh rate switching is not
      * allowed.
@@ -1558,6 +1600,13 @@
     }
 
     /**
+     * @return The refresh rate thermal map for high blocking zone.
+     */
+    public SparseArray<SurfaceControl.RefreshRateRange> getHighBlockingZoneThermalMap() {
+        return getThermalRefreshRateThrottlingData(mHighBlockingZoneThermalMapId);
+    }
+
+    /**
      * @return A mapping from screen off brightness sensor readings to lux values. This estimates
      * the ambient lux when the screen is off to determine the initial brightness
      */
@@ -1664,6 +1713,10 @@
                 + mAutoBrightnessBrighteningLightDebounce
                 + ", mAutoBrightnessDarkeningLightDebounce= "
                 + mAutoBrightnessDarkeningLightDebounce
+                + ", mAutoBrightnessBrighteningLightDebounceIdle= "
+                + mAutoBrightnessBrighteningLightDebounceIdle
+                + ", mAutoBrightnessDarkeningLightDebounceIdle= "
+                + mAutoBrightnessDarkeningLightDebounceIdle
                 + ", mBrightnessLevelsLux= " + Arrays.toString(mBrightnessLevelsLux)
                 + ", mBrightnessLevelsNits= " + Arrays.toString(mBrightnessLevelsNits)
                 + ", mDdcAutoBrightnessAvailable= " + mDdcAutoBrightnessAvailable
@@ -1677,6 +1730,8 @@
                 + ", mDefaultRefreshRateInHbmHdr= " + mDefaultRefreshRateInHbmHdr
                 + ", mDefaultRefreshRateInHbmSunlight= " + mDefaultRefreshRateInHbmSunlight
                 + ", mRefreshRateThrottlingMap= " + mRefreshRateThrottlingMap
+                + ", mLowBlockingZoneThermalMapId= " + mLowBlockingZoneThermalMapId
+                + ", mHighBlockingZoneThermalMapId= " + mHighBlockingZoneThermalMapId
                 + "\n"
                 + "mLowDisplayBrightnessThresholds= "
                 + Arrays.toString(mLowDisplayBrightnessThresholds)
@@ -2127,9 +2182,13 @@
     }
 
     /**
-     * Loads the refresh rate configurations pertaining to the upper blocking zones.
+     * Loads the refresh rate configurations pertaining to the lower blocking zones.
      */
     private void loadLowerRefreshRateBlockingZones(BlockingZoneConfig lowerBlockingZoneConfig) {
+        if (lowerBlockingZoneConfig != null) {
+            mLowBlockingZoneThermalMapId =
+                    lowerBlockingZoneConfig.getRefreshRateThermalThrottlingId();
+        }
         loadLowerBlockingZoneDefaultRefreshRate(lowerBlockingZoneConfig);
         loadLowerBrightnessThresholds(lowerBlockingZoneConfig);
     }
@@ -2138,6 +2197,10 @@
      * Loads the refresh rate configurations pertaining to the upper blocking zones.
      */
     private void loadHigherRefreshRateBlockingZones(BlockingZoneConfig upperBlockingZoneConfig) {
+        if (upperBlockingZoneConfig != null) {
+            mHighBlockingZoneThermalMapId =
+                    upperBlockingZoneConfig.getRefreshRateThermalThrottlingId();
+        }
         loadHigherBlockingZoneDefaultRefreshRate(upperBlockingZoneConfig);
         loadHigherBrightnessThresholds(upperBlockingZoneConfig);
     }
@@ -2277,6 +2340,9 @@
         final AutoBrightness autoBrightness = config.getAutoBrightness();
         loadAutoBrightnessBrighteningLightDebounce(autoBrightness);
         loadAutoBrightnessDarkeningLightDebounce(autoBrightness);
+        // Idle must be called after interactive, since we fall back to it if needed.
+        loadAutoBrightnessBrighteningLightDebounceIdle(autoBrightness);
+        loadAutoBrightnessDarkeningLightDebounceIdle(autoBrightness);
         loadAutoBrightnessDisplayBrightnessMapping(autoBrightness);
         loadEnableAutoBrightness(autoBrightness);
     }
@@ -2312,6 +2378,37 @@
     }
 
     /**
+     * Loads the auto-brightness brightening light debounce for idle mode. Internally, this takes
+     * care of loading the value from the display config, and if not present, falls back to
+     * whichever interactive value was chosen.
+     */
+    private void loadAutoBrightnessBrighteningLightDebounceIdle(
+            AutoBrightness autoBrightnessConfig) {
+        if (autoBrightnessConfig == null
+                || autoBrightnessConfig.getBrighteningLightDebounceIdleMillis() == null) {
+            mAutoBrightnessBrighteningLightDebounceIdle = mAutoBrightnessBrighteningLightDebounce;
+        } else {
+            mAutoBrightnessBrighteningLightDebounceIdle =
+                    autoBrightnessConfig.getBrighteningLightDebounceIdleMillis().intValue();
+        }
+    }
+
+    /**
+     * Loads the auto-brightness darkening light debounce for idle mode. Internally, this takes
+     * care of loading the value from the display config, and if not present, falls back to
+     * whichever interactive value was chosen.
+     */
+    private void loadAutoBrightnessDarkeningLightDebounceIdle(AutoBrightness autoBrightnessConfig) {
+        if (autoBrightnessConfig == null
+                || autoBrightnessConfig.getDarkeningLightDebounceIdleMillis() == null) {
+            mAutoBrightnessDarkeningLightDebounceIdle = mAutoBrightnessDarkeningLightDebounce;
+        } else {
+            mAutoBrightnessDarkeningLightDebounceIdle =
+                    autoBrightnessConfig.getDarkeningLightDebounceIdleMillis().intValue();
+        }
+    }
+
+    /**
      * Loads the auto-brightness display brightness mappings. Internally, this takes care of
      * loading the value from the display config, and if not present, falls back to config.xml.
      */
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dd22cfd..79b7343 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1232,6 +1232,10 @@
                     .getAutoBrightnessBrighteningLightDebounce();
             long darkeningLightDebounce = mDisplayDeviceConfig
                     .getAutoBrightnessDarkeningLightDebounce();
+            long brighteningLightDebounceIdle = mDisplayDeviceConfig
+                    .getAutoBrightnessBrighteningLightDebounceIdle();
+            long darkeningLightDebounceIdle = mDisplayDeviceConfig
+                    .getAutoBrightnessDarkeningLightDebounceIdle();
             boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
                     com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
 
@@ -1271,7 +1275,8 @@
                     mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
                     PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
                     lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
-                    darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+                    darkeningLightDebounce, brighteningLightDebounceIdle,
+                    darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp,
                     ambientBrightnessThresholds, screenBrightnessThresholds,
                     ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
                     mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper,
@@ -1906,6 +1911,7 @@
 
             final float currentBrightness = mPowerState.getScreenBrightness();
             final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
+
             if (isValidBrightnessValue(animateValue)
                     && (animateValue != currentBrightness
                     || sdrAnimateValue != currentSdrBrightness)) {
@@ -3536,6 +3542,7 @@
                 int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
                 float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
                 long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+                long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
                 boolean resetAmbientLuxAfterWarmUpConfig,
                 HysteresisLevels ambientBrightnessThresholds,
                 HysteresisLevels screenBrightnessThresholds,
@@ -3549,6 +3556,7 @@
                     interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin,
                     brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
                     brighteningLightDebounceConfig, darkeningLightDebounceConfig,
+                    brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
                     resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
                     screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                     screenBrightnessThresholdsIdle, context, brightnessRangeController,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0f89a6e..6c2240b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -1043,6 +1043,10 @@
                     .getAutoBrightnessBrighteningLightDebounce();
             long darkeningLightDebounce = mDisplayDeviceConfig
                     .getAutoBrightnessDarkeningLightDebounce();
+            long brighteningLightDebounceIdle = mDisplayDeviceConfig
+                    .getAutoBrightnessBrighteningLightDebounceIdle();
+            long darkeningLightDebounceIdle = mDisplayDeviceConfig
+                    .getAutoBrightnessDarkeningLightDebounceIdle();
             boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
                     R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
 
@@ -1082,7 +1086,8 @@
                     mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
                     PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
                     lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
-                    darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+                    darkeningLightDebounce, brighteningLightDebounceIdle,
+                    darkeningLightDebounceIdle, autoBrightnessResetAmbientLuxAfterWarmUp,
                     ambientBrightnessThresholds, screenBrightnessThresholds,
                     ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
                     mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper,
@@ -2886,6 +2891,7 @@
                 int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
                 float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
                 long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+                long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
                 boolean resetAmbientLuxAfterWarmUpConfig,
                 HysteresisLevels ambientBrightnessThresholds,
                 HysteresisLevels screenBrightnessThresholds,
@@ -2899,6 +2905,7 @@
                     interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin,
                     brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate,
                     brighteningLightDebounceConfig, darkeningLightDebounceConfig,
+                    brighteningLightDebounceConfigIdle, darkeningLightDebounceConfigIdle,
                     resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
                     screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                     screenBrightnessThresholdsIdle, context, brightnessModeController,
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index f15f036..f08878f 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1540,6 +1540,21 @@
         private final Injector mInjector;
         private final Handler mHandler;
 
+        private final IThermalEventListener.Stub mThermalListener =
+                new IThermalEventListener.Stub() {
+                    @Override
+                    public void notifyThrottling(Temperature temp) {
+                        @Temperature.ThrottlingStatus int currentStatus = temp.getStatus();
+                        synchronized (mLock) {
+                            if (mThermalStatus != currentStatus) {
+                                mThermalStatus = currentStatus;
+                            }
+                            onBrightnessChangedLocked();
+                        }
+                    }
+                };
+        private boolean mThermalRegistered;
+
         // Enable light sensor only when mShouldObserveAmbientLowChange is true or
         // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
         // changeable and low power mode off. After initialization, these states will
@@ -1548,9 +1563,17 @@
         private boolean mRefreshRateChangeable = false;
         private boolean mLowPowerModeEnabled = false;
 
+        @Nullable
+        private SparseArray<RefreshRateRange> mLowZoneRefreshRateForThermals;
         private int mRefreshRateInLowZone;
+
+        @Nullable
+        private SparseArray<RefreshRateRange> mHighZoneRefreshRateForThermals;
         private int mRefreshRateInHighZone;
 
+        @GuardedBy("mLock")
+        private @Temperature.ThrottlingStatus int mThermalStatus = Temperature.THROTTLING_NONE;
+
         BrightnessObserver(Context context, Handler handler, Injector injector) {
             mContext = context;
             mHandler = handler;
@@ -1649,6 +1672,8 @@
                                 R.integer.config_defaultRefreshRateInZone)
                         : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
             }
+            mLowZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+                    : displayDeviceConfig.getLowBlockingZoneThermalMap();
             mRefreshRateInLowZone = refreshRateInLowZone;
         }
 
@@ -1668,6 +1693,8 @@
                                 R.integer.config_fixedRefreshRateInHighZone)
                         : displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate();
             }
+            mHighZoneRefreshRateForThermals = displayDeviceConfig == null ? null
+                    : displayDeviceConfig.getHighBlockingZoneThermalMap();
             mRefreshRateInHighZone = refreshRateInHighZone;
         }
 
@@ -2117,6 +2144,15 @@
             if (insideLowZone) {
                 refreshRateVote =
                         Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+                if (mLowZoneRefreshRateForThermals != null) {
+                    RefreshRateRange range = SkinThermalStatusObserver
+                            .findBestMatchingRefreshRateRange(mThermalStatus,
+                                    mLowZoneRefreshRateForThermals);
+                    if (range != null) {
+                        refreshRateVote =
+                                Vote.forPhysicalRefreshRates(range.min, range.max);
+                    }
+                }
                 refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
             }
 
@@ -2126,6 +2162,15 @@
                 refreshRateVote =
                         Vote.forPhysicalRefreshRates(mRefreshRateInHighZone,
                                 mRefreshRateInHighZone);
+                if (mHighZoneRefreshRateForThermals != null) {
+                    RefreshRateRange range = SkinThermalStatusObserver
+                            .findBestMatchingRefreshRateRange(mThermalStatus,
+                                    mHighZoneRefreshRateForThermals);
+                    if (range != null) {
+                        refreshRateVote =
+                                Vote.forPhysicalRefreshRates(range.min, range.max);
+                    }
+                }
                 refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
             }
 
@@ -2184,13 +2229,25 @@
                         + mRefreshRateChangeable);
             }
 
+            boolean registerForThermals = false;
             if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
                      && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
                 registerLightSensor();
-
+                registerForThermals = mLowZoneRefreshRateForThermals != null
+                        || mHighZoneRefreshRateForThermals != null;
             } else {
                 unregisterSensorListener();
             }
+
+            if (registerForThermals && !mThermalRegistered) {
+                mThermalRegistered = mInjector.registerThermalServiceListener(mThermalListener);
+            } else if (!registerForThermals && mThermalRegistered) {
+                mInjector.unregisterThermalServiceListener(mThermalListener);
+                mThermalRegistered = false;
+                synchronized (mLock) {
+                    mThermalStatus = Temperature.THROTTLING_NONE; // reset
+                }
+            }
         }
 
         private void registerLightSensor() {
@@ -2821,6 +2878,7 @@
         boolean isDozeState(Display d);
 
         boolean registerThermalServiceListener(IThermalEventListener listener);
+        void unregisterThermalServiceListener(IThermalEventListener listener);
 
         boolean supportsFrameRateOverride();
 
@@ -2922,6 +2980,19 @@
         }
 
         @Override
+        public void unregisterThermalServiceListener(IThermalEventListener listener) {
+            IThermalService thermalService = getThermalService();
+            if (thermalService == null) {
+                Slog.w(TAG, "Could not unregister thermal status. Service not available");
+            }
+            try {
+                thermalService.unregisterThermalEventListener(listener);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to unregister thermal status listener", e);
+            }
+        }
+
+        @Override
         public boolean supportsFrameRateOverride() {
             return SurfaceFlingerProperties.enable_frame_rate_override().orElse(true);
         }
diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
index 58e1550..b29cda8 100644
--- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
+++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
@@ -64,6 +64,20 @@
         mHandler = handler;
     }
 
+    @Nullable
+    public static SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
+            @Temperature.ThrottlingStatus int currentStatus,
+            SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
+        SurfaceControl.RefreshRateRange foundRange = null;
+        for (int status = currentStatus; status >= 0; status--) {
+            foundRange = throttlingMap.get(status);
+            if (foundRange != null) {
+                break;
+            }
+        }
+        return foundRange;
+    }
+
     void observe() {
         // if failed to register thermal service listener, don't register display listener
         if (!mInjector.registerThermalServiceListener(this)) {
@@ -228,20 +242,6 @@
         }
     }
 
-    @Nullable
-    private SurfaceControl.RefreshRateRange findBestMatchingRefreshRateRange(
-            @Temperature.ThrottlingStatus int currentStatus,
-            SparseArray<SurfaceControl.RefreshRateRange> throttlingMap) {
-        SurfaceControl.RefreshRateRange foundRange = null;
-        for (int status = currentStatus; status >= 0; status--) {
-            foundRange = throttlingMap.get(status);
-            if (foundRange != null) {
-                break;
-            }
-        }
-        return foundRange;
-    }
-
     private void fallbackReportThrottlingIfNeeded(int displayId,
             @Temperature.ThrottlingStatus int currentStatus) {
         Vote vote = null;
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index d238dae..2ede56d 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -67,7 +67,7 @@
 
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
-        t.setLayer(mInputSurface, Integer.MAX_VALUE);
+        t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
         t.setPosition(mInputSurface, 0, 0);
         t.setCrop(mInputSurface, null /* crop to parent surface */);
         t.show(mInputSurface);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index b8e9d5d..62660c4 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -381,6 +381,17 @@
     public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
     public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
 
+    // The following are layer numbers used for z-ordering the input overlay layers on the display.
+    // This is used for ordering layers inside {@code DisplayContent#getInputOverlayLayer()}.
+    //
+    // The layer where gesture monitors are added.
+    public static final int INPUT_OVERLAY_LAYER_GESTURE_MONITOR = 1;
+    // Place the handwriting layer above gesture monitors so that styluses cannot trigger
+    // system gestures (e.g. navigation bar, edge-back, etc) while there is an active
+    // handwriting session.
+    public static final int INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE = 2;
+
+
     private final String mVelocityTrackerStrategy;
 
     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 0c889c2..7726f40 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -27,16 +27,13 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
+import com.android.server.input.InputManagerService;
+
 final class HandwritingEventReceiverSurface {
 
     public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
     static final boolean DEBUG = HandwritingModeController.DEBUG;
 
-    // Place the layer at the highest layer so stylus cannot trigger gesture monitors
-    // (e.g. navigation bar, edge-back, etc) while handwriting is ongoing.
-    // TODO(b/217538817): Specify the ordering in WM by usage.
-    private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE;
-
     private final InputWindowHandle mWindowHandle;
     private final InputChannel mClientChannel;
     private final SurfaceControl mInputSurface;
@@ -68,7 +65,7 @@
 
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
-        t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER);
+        t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
         t.setPosition(mInputSurface, 0, 0);
         t.setCrop(mInputSurface, null /* crop to parent surface */);
         t.show(mInputSurface);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8e7baf2..1ec8b10 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3275,15 +3275,18 @@
             } else {
                 // If subtype is null, try to find the most applicable one from
                 // getCurrentInputMethodSubtype.
+                subtypeId = NOT_A_SUBTYPE_ID;
                 newSubtype = getCurrentInputMethodSubtypeLocked();
+                if (newSubtype != null) {
+                    for (int i = 0; i < subtypeCount; ++i) {
+                        if (Objects.equals(newSubtype, info.getSubtypeAt(i))) {
+                            subtypeId = i;
+                            break;
+                        }
+                    }
+                }
             }
-            if (newSubtype == null || oldSubtype == null) {
-                Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
-                        + ", new subtype = " + newSubtype);
-                notifyInputMethodSubtypeChangedLocked(userId, info, null);
-                return;
-            }
-            if (!newSubtype.equals(oldSubtype)) {
+            if (!Objects.equals(newSubtype, oldSubtype)) {
                 setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
                 IInputMethodInvoker curMethod = getCurMethodLocked();
                 if (curMethod != null) {
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index ec03d9d..a7c986d 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -710,7 +710,7 @@
     }
 
     private String getCallingRulePusherPackageName(int callingUid) {
-        // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages.
+        // Obtain the system apps that are allowlisted in config_integrityRuleProviderPackages.
         List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps();
         if (DEBUG_INTEGRITY_COMPONENT) {
             Slog.i(
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d700c6a..8e9c21f 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -77,7 +77,6 @@
 import java.util.Objects;
 import java.util.Set;
 
-
 /**
  * A class that manages a user's synthetic password (SP) ({@link #SyntheticPassword}), along with a
  * set of SP protectors that are independent ways that the SP is protected.
@@ -552,22 +551,48 @@
         }
     }
 
-    private @Nullable IWeaver getWeaverServiceInternal() {
-        // Try to get the AIDL service first
+    private @Nullable IWeaver getWeaverAidlService() {
+        final IWeaver aidlWeaver;
         try {
-            IWeaver aidlWeaver = IWeaver.Stub.asInterface(
-                    ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
-            if (aidlWeaver != null) {
-                Slog.i(TAG, "Using AIDL weaver service");
-                try {
-                    aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Unable to register Weaver death recipient", e);
-                }
-                return aidlWeaver;
-            }
+            aidlWeaver =
+                    IWeaver.Stub.asInterface(
+                            ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default"));
         } catch (SecurityException e) {
             Slog.w(TAG, "Does not have permissions to get AIDL weaver service");
+            return null;
+        }
+        if (aidlWeaver == null) {
+            return null;
+        }
+        final int aidlVersion;
+        try {
+            aidlVersion = aidlWeaver.getInterfaceVersion();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Cannot get AIDL weaver service version", e);
+            return null;
+        }
+        if (aidlVersion < 2) {
+            Slog.w(TAG,
+                    "Ignoring AIDL weaver service v"
+                            + aidlVersion
+                            + " because only v2 and later are supported");
+            return null;
+        }
+        Slog.i(TAG, "Found AIDL weaver service v" + aidlVersion);
+        return aidlWeaver;
+    }
+
+    private @Nullable IWeaver getWeaverServiceInternal() {
+        // Try to get the AIDL service first
+        IWeaver aidlWeaver = getWeaverAidlService();
+        if (aidlWeaver != null) {
+            Slog.i(TAG, "Using AIDL weaver service");
+            try {
+                aidlWeaver.asBinder().linkToDeath(new WeaverDiedRecipient(), 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to register Weaver death recipient", e);
+            }
+            return aidlWeaver;
         }
 
         // If the AIDL service can't be found, look for the HIDL service
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 37bcfbb..5b87069 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -313,7 +313,7 @@
         if (linkedItemLandingComponent != null) {
             int callingUid = Binder.getCallingUid();
             MediaServerUtils.enforcePackageName(
-                    linkedItemLandingComponent.getPackageName(), callingUid);
+                    mContext, linkedItemLandingComponent.getPackageName(), callingUid);
             if (!MediaServerUtils.isValidActivityComponentName(
                     mContext,
                     linkedItemLandingComponent,
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index 60592fe..6a954d6 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -31,6 +31,7 @@
 import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.List;
 
 /** Util class for media server. */
@@ -63,7 +64,8 @@
      * @throws IllegalArgumentException If the given {@code packageName} does not correspond to the
      *     given {@code uid}, and {@code uid} is not the root uid, or the shell uid.
      */
-    public static void enforcePackageName(String packageName, int uid) {
+    public static void enforcePackageName(
+            @NonNull Context context, @NonNull String packageName, int uid) {
         if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
             return;
         }
@@ -76,12 +78,16 @@
                 packageManagerInternal.getPackageUid(
                         packageName, 0 /* flags */, UserHandle.getUserId(uid));
         if (!UserHandle.isSameApp(uid, actualUid)) {
+            String[] uidPackages = context.getPackageManager().getPackagesForUid(uid);
             throw new IllegalArgumentException(
                     "packageName does not belong to the calling uid; "
                             + "pkg="
                             + packageName
                             + ", uid="
-                            + uid);
+                            + uid
+                            + " ("
+                            + Arrays.toString(uidPackages)
+                            + ")");
         }
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f2242bf..f4c9518 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -542,7 +542,7 @@
             int callingPid, int callingUid, String callingPackage, String reason) {
         final long token = Binder.clearCallingIdentity();
         try {
-            MediaServerUtils.enforcePackageName(callingPackage, callingUid);
+            MediaServerUtils.enforcePackageName(mContext, callingPackage, callingUid);
             if (targetUid != callingUid) {
                 boolean canAllowWhileInUse = mActivityManagerLocal
                         .canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
@@ -1187,7 +1187,7 @@
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaServerUtils.enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(mContext, packageName, uid);
                 int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
                 if (cb == null) {
                     throw new IllegalArgumentException("Controller callback cannot be null");
@@ -1239,7 +1239,7 @@
             final int userId = userHandle.getIdentifier();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaServerUtils.enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(mContext, packageName, uid);
                 enforceMediaPermissions(packageName, pid, uid, userId);
 
                 MediaSessionRecordImpl record;
@@ -1270,7 +1270,7 @@
             final int userId = userHandle.getIdentifier();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaServerUtils.enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(mContext, packageName, uid);
                 enforceMediaPermissions(packageName, pid, uid, userId);
 
                 MediaSessionRecordImpl record;
@@ -1596,7 +1596,7 @@
             final int userId = userHandle.getIdentifier();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaServerUtils.enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(mContext, packageName, uid);
                 enforceMediaPermissions(packageName, pid, uid, userId);
 
                 synchronized (mLock) {
@@ -2110,7 +2110,7 @@
                 // If they gave us a component name verify they own the
                 // package
                 packageName = componentName.getPackageName();
-                MediaServerUtils.enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(mContext, packageName, uid);
             }
             // Check that they can make calls on behalf of the user and get the final user id
             int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 009cc3b..6f0a4b4 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1928,6 +1928,7 @@
                 mConditionProviders.onUserRemoved(userId);
                 mAssistants.onUserRemoved(userId);
                 mHistoryManager.onUserRemoved(userId);
+                mPreferencesHelper.syncChannelsBypassingDnd();
                 handleSavePolicyFile();
             } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
@@ -2379,6 +2380,7 @@
         });
         mPermissionHelper = permissionHelper;
         mNotificationChannelLogger = channelLogger;
+        mUserProfiles.updateCache(getContext());
         mPreferencesHelper = new PreferencesHelper(getContext(),
                 mPackageManagerClient,
                 mRankingHandler,
@@ -2387,6 +2389,7 @@
                 mPermissionManager,
                 mNotificationChannelLogger,
                 mAppOps,
+                mUserProfiles,
                 new SysUiStatsEvent.BuilderFactory(),
                 mShowReviewPermissionsNotification);
         mRankingHelper = new RankingHelper(getContext(),
@@ -2442,8 +2445,6 @@
         mZenModeHelper.initZenMode();
         mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
 
-        mUserProfiles.updateCache(getContext());
-
         if (mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             telephonyManager.listen(new PhoneStateListener() {
                 @Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3f799dc..0e37f10 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -38,7 +38,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -53,7 +52,6 @@
 import android.content.pm.UserInfo;
 import android.metrics.LogMaker;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
@@ -114,7 +112,6 @@
     private static final int XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION = 4;
     @VisibleForTesting
     static final int UNKNOWN_UID = UserHandle.USER_NULL;
-    private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
 
     @VisibleForTesting
     static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
@@ -196,6 +193,7 @@
     private final PermissionManager mPermissionManager;
     private final NotificationChannelLogger mNotificationChannelLogger;
     private final AppOpsManager mAppOps;
+    private final ManagedServices.UserProfiles mUserProfiles;
 
     private SparseBooleanArray mBadgingEnabled;
     private SparseBooleanArray mBubblesEnabled;
@@ -204,14 +202,12 @@
     private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING;
     private boolean mCurrentUserHasChannelsBypassingDnd;
     private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
-    private boolean mShowReviewPermissionsNotification;
-
-    private boolean mAllowInvalidShortcuts = false;
+    private final boolean mShowReviewPermissionsNotification;
 
     public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
             ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
             NotificationChannelLogger notificationChannelLogger,
-            AppOpsManager appOpsManager,
+            AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles,
             SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
             boolean showReviewPermissionsNotification) {
         mContext = context;
@@ -222,6 +218,7 @@
         mPm = pm;
         mNotificationChannelLogger = notificationChannelLogger;
         mAppOps = appOpsManager;
+        mUserProfiles = userProfiles;
         mStatsEventBuilderFactory = statsEventBuilderFactory;
         mShowReviewPermissionsNotification = showReviewPermissionsNotification;
 
@@ -435,7 +432,7 @@
                 channel.getConversationId() != null &&
                         channel.getConversationId().contains(
                                 PLACEHOLDER_CONVERSATION_ID);
-        return mAllowInvalidShortcuts || (!mAllowInvalidShortcuts && !isInvalidShortcutChannel);
+        return !isInvalidShortcutChannel;
     }
 
     private boolean isDeletionOk(NotificationChannel nc) {
@@ -1790,8 +1787,9 @@
      * Syncs {@link #mCurrentUserHasChannelsBypassingDnd} with the current user's notification
      * policy before updating. Must be called:
      * <ul>
-     *     <li>On system init, after channels and DND configurations are loaded.</li>
-     *     <li>When the current user changes, after the corresponding DND config is loaded.</li>
+     *     <li>On system init, after channels and DND configurations are loaded.
+     *     <li>When the current user is switched, after the corresponding DND config is loaded.
+     *     <li>If users are removed (the removed user could've been a profile of the current one).
      * </ul>
      */
     void syncChannelsBypassingDnd() {
@@ -1805,20 +1803,19 @@
     /**
      * Updates the user's NotificationPolicy based on whether the current userId has channels
      * bypassing DND. It should be called whenever a channel is created, updated, or deleted, or
-     * when the current user is switched.
+     * when the current user (or its profiles) change.
      */
     private void updateCurrentUserHasChannelsBypassingDnd(int callingUid,
             boolean fromSystemOrSystemUi) {
         ArraySet<Pair<String, Integer>> candidatePkgs = new ArraySet<>();
 
-        final int currentUserId = getCurrentUser();
+        final IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
         synchronized (mPackagePreferences) {
             final int numPackagePreferences = mPackagePreferences.size();
             for (int i = 0; i < numPackagePreferences; i++) {
                 final PackagePreferences r = mPackagePreferences.valueAt(i);
-                // Package isn't associated with the current userId
-                if (currentUserId != UserHandle.getUserId(r.uid)) {
-                    continue;
+                if (!currentUserIds.contains(UserHandle.getUserId(r.uid))) {
+                    continue; // Package isn't associated with any profile of the current userId.
                 }
 
                 for (NotificationChannel channel : r.channels.values()) {
@@ -1842,13 +1839,6 @@
         }
     }
 
-    private int getCurrentUser() {
-        final long identity = Binder.clearCallingIdentity();
-        int currentUserId = ActivityManager.getCurrentUser();
-        Binder.restoreCallingIdentity(identity);
-        return currentUserId;
-    }
-
     private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
         // Channel is in a group that's blocked
         if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b5ec136..66a1703 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -459,8 +459,8 @@
                     // Do not uninstall the APK if an app should be cached
                     boolean keepUninstalledPackage =
                             mPm.shouldKeepUninstalledPackageLPr(packageName);
-                    if (ps.isAnyInstalled(
-                            mUserManagerInternal.getUserIds()) || keepUninstalledPackage) {
+                    if (ps.isInstalledOrHasDataOnAnyOtherUser(
+                            mUserManagerInternal.getUserIds(), userId) || keepUninstalledPackage) {
                         // Other users still have this package installed, so all
                         // we need to do is clear this user's data and save that
                         // it is uninstalled.
@@ -555,6 +555,7 @@
             if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
                 mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
                         FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
+                ps.setCeDataInode(-1, nextUserId);
             }
             mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
             preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
@@ -615,7 +616,9 @@
                 Slog.d(TAG, "Marking package:" + ps.getPackageName()
                         + " uninstalled for user:" + nextUserId);
             }
-            ps.setUserState(nextUserId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
+            ps.setUserState(nextUserId,
+                    ps.getCeDataInode(nextUserId),
+                    COMPONENT_ENABLED_STATE_DEFAULT,
                     false /*installed*/,
                     true /*stopped*/,
                     true /*notLaunched*/,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c63d8be..f0bbd35 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5179,9 +5179,6 @@
         public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
             final Computer snapshot = snapshotComputer();
             final int callingUid = Binder.getCallingUid();
-            snapshot.enforceCrossUserPermission(
-                    callingUid, userId, false /* requireFullPermission */,
-                    false /* checkShell */, "getUserMinAspectRatio");
             final PackageStateInternal packageState = snapshot
                     .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
             return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 14693a6..7cac3e1 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -809,9 +809,16 @@
         return changed;
     }
 
-    boolean isAnyInstalled(int[] users) {
-        for (int user: users) {
-            if (readUserState(user).isInstalled()) {
+    boolean isInstalledOrHasDataOnAnyOtherUser(int[] allUsers, int currentUser) {
+        for (int user: allUsers) {
+            if (user == currentUser) {
+                continue;
+            }
+            final PackageUserStateInternal userState = readUserState(user);
+            if (userState.isInstalled()) {
+                return true;
+            }
+            if (userState.getCeDataInode() > 0) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b6da462..307867c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5186,9 +5186,12 @@
             pw.print(" instant=");
             pw.print(userState.isInstantApp());
             pw.print(" virtual=");
-            pw.println(userState.isVirtualPreload());
+            pw.print(userState.isVirtualPreload());
             pw.print(" quarantined=");
             pw.print(userState.isQuarantined());
+
+            // Dump install state with additional indentation on their own lines.
+            pw.println();
             pw.print("      installReason=");
             pw.println(userState.getInstallReason());
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2f68021..b3aa09b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -29,6 +29,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION_CODES.M;
 import static android.os.Build.VERSION_CODES.O;
+import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
@@ -338,6 +339,7 @@
     // The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS
     static final int SHORT_PRESS_PRIMARY_NOTHING = 0;
     static final int SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS = 1;
+    static final int SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY = 2;
 
     // Must match: config_longPressOnStemPrimaryBehavior in config.xml
     // The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS
@@ -609,6 +611,9 @@
     // What we do when the user double-taps on home
     private int mDoubleTapOnHomeBehavior;
 
+    // Must match config_primaryShortPressTargetActivity in config.xml
+    ComponentName mPrimaryShortPressTargetActivity;
+
     // Whether to lock the device after the next dreaming transition has finished.
     private boolean mLockAfterDreamingTransitionFinished;
 
@@ -1374,7 +1379,7 @@
                 mPowerKeyHandled = true;
                 performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
                         "Power - Long Press - Go To Assistant");
-                final int powerKeyDeviceId = Integer.MIN_VALUE;
+                final int powerKeyDeviceId = INVALID_INPUT_DEVICE_ID;
                 launchAssistAction(null, powerKeyDeviceId, eventTime,
                         AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
                 break;
@@ -1461,23 +1466,59 @@
     }
 
     private void stemPrimarySinglePressAction(int behavior) {
+        if (DEBUG_INPUT) {
+            Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
+        }
+        if (behavior == SHORT_PRESS_PRIMARY_NOTHING) return;
+
+        final boolean keyguardActive = mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
+        if (keyguardActive) {
+            // If keyguarded then notify the keyguard.
+            mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+            return;
+        }
         switch (behavior) {
-            case SHORT_PRESS_PRIMARY_NOTHING:
-                break;
             case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
                 if (DEBUG_INPUT) {
                     Slog.d(TAG, "Executing stem primary short press action behavior.");
                 }
-                final boolean keyguardActive =
-                        mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
-                if (!keyguardActive) {
-                    Intent intent = new Intent(Intent.ACTION_ALL_APPS);
-                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
+                allAppsIntent.addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK
+                                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                startActivityAsUser(allAppsIntent, UserHandle.CURRENT_OR_SELF);
+                break;
+            case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
+                if (DEBUG_INPUT) {
+                    Slog.d(
+                            TAG,
+                            "Executing stem primary short press action behavior for launching "
+                                    + "target activity.");
+                }
+                if (mPrimaryShortPressTargetActivity != null) {
+                    Intent targetActivityIntent = new Intent();
+                    targetActivityIntent.setComponent(mPrimaryShortPressTargetActivity);
+                    ResolveInfo resolveInfo =
+                            mContext.getPackageManager()
+                                    .resolveActivity(targetActivityIntent, /* flags= */ 0);
+                    if (resolveInfo != null) {
+                        targetActivityIntent.addFlags(
+                                Intent.FLAG_ACTIVITY_NEW_TASK
+                                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                                        | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+                        startActivityAsUser(targetActivityIntent, UserHandle.CURRENT_OR_SELF);
+                    } else {
+                        Slog.wtf(
+                                TAG,
+                                "Could not resolve activity with : "
+                                        + mPrimaryShortPressTargetActivity.flattenToString()
+                                        + " name.");
+                    }
                 } else {
-                    // If keyguarded then notify the keyguard.
-                    mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+                    Slog.wtf(
+                            TAG,
+                            "mPrimaryShortPressTargetActivity must not be null and correctly"
+                                + " specified");
                 }
                 break;
         }
@@ -1527,7 +1568,7 @@
         }
     }
 
-    private void stemPrimaryLongPress() {
+    private void stemPrimaryLongPress(long eventTime) {
         if (DEBUG_INPUT) {
             Slog.d(TAG, "Executing stem primary long press action behavior.");
         }
@@ -1536,7 +1577,12 @@
             case LONG_PRESS_PRIMARY_NOTHING:
                 break;
             case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
-                launchVoiceAssist(/* allowDuringSetup= */false);
+                final int stemPrimaryKeyDeviceId = INVALID_INPUT_DEVICE_ID;
+                launchAssistAction(
+                        null,
+                        stemPrimaryKeyDeviceId,
+                        eventTime,
+                        AssistUtils.INVOCATION_TYPE_UNKNOWN);
                 break;
         }
     }
@@ -2159,6 +2205,7 @@
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
+        mSearchManager = mContext.getSystemService(SearchManager.class);
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -2295,6 +2342,9 @@
         mPowerDoublePressTargetActivity = ComponentName.unflattenFromString(
             mContext.getResources().getString(
                 com.android.internal.R.string.config_doublePressOnPowerTargetActivity));
+        mPrimaryShortPressTargetActivity = ComponentName.unflattenFromString(
+            mContext.getResources().getString(
+                com.android.internal.R.string.config_primaryShortPressTargetActivity));
         mShortPressOnSleepBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_shortPressOnSleepBehavior);
         mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
@@ -2658,7 +2708,7 @@
 
         @Override
         void onLongPress(long eventTime) {
-            stemPrimaryLongPress();
+            stemPrimaryLongPress(eventTime);
         }
 
         @Override
@@ -3905,7 +3955,7 @@
         // Add Intent Extra data.
         Bundle args = null;
         args = new Bundle();
-        if (deviceId > Integer.MIN_VALUE) {
+        if (deviceId != INVALID_INPUT_DEVICE_ID) {
             args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, deviceId);
         }
         if (hint != null) {
@@ -3914,8 +3964,15 @@
         args.putLong(Intent.EXTRA_TIME, eventTime);
         args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
 
-        ((SearchManager) mContext.createContextAsUser(UserHandle.of(mCurrentUserId), 0)
-                .getSystemService(Context.SEARCH_SERVICE)).launchAssist(args);
+        if (mSearchManager != null) {
+            mSearchManager.launchAssist(args);
+        } else {
+            // Fallback to status bar if search manager doesn't exist (e.g. on wear).
+            StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+            if (statusBar != null) {
+                statusBar.startAssist(args);
+            }
+        }
     }
 
     /**
@@ -3926,39 +3983,16 @@
         final boolean keyguardActive =
                 mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
         if (!keyguardActive) {
-            if (mHasFeatureWatch && isInRetailMode()) {
-                launchRetailVoiceAssist(allowDuringSetup);
-            } else {
-                startVoiceAssistIntent(allowDuringSetup);
-            }
+            startActivityAsUser(
+                    new Intent(Intent.ACTION_VOICE_ASSIST),
+                    /* bundle= */ null,
+                    UserHandle.CURRENT_OR_SELF,
+                    allowDuringSetup);
         } else {
             mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
         }
     }
 
-    private void launchRetailVoiceAssist(boolean allowDuringSetup) {
-        Intent retailIntent = new Intent(ACTION_VOICE_ASSIST_RETAIL);
-        ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
-                retailIntent, /* flags= */0);
-        if (resolveInfo != null) {
-            retailIntent.setComponent(
-                    new ComponentName(resolveInfo.activityInfo.packageName,
-                            resolveInfo.activityInfo.name));
-            startActivityAsUser(retailIntent, null, UserHandle.CURRENT_OR_SELF,
-                    allowDuringSetup);
-        } else {
-            Slog.w(TAG, "Couldn't find an app to process " + ACTION_VOICE_ASSIST_RETAIL
-                    + ". Fall back to start " + Intent.ACTION_VOICE_ASSIST);
-            startVoiceAssistIntent(allowDuringSetup);
-        }
-    }
-
-    private void startVoiceAssistIntent(boolean allowDuringSetup) {
-        Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
-        startActivityAsUser(intent, null, UserHandle.CURRENT_OR_SELF,
-                allowDuringSetup);
-    }
-
     private boolean isInRetailMode() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
@@ -3981,13 +4015,6 @@
         }
     }
 
-    private SearchManager getSearchManager() {
-        if (mSearchManager == null) {
-            mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
-        }
-        return mSearchManager;
-    }
-
     private void preloadRecentApps() {
         mPreloadedRecentApps = true;
         StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -6471,6 +6498,8 @@
                 return "SHORT_PRESS_PRIMARY_NOTHING";
             case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
                 return "SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS";
+            case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
+                return "SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY";
             default:
                 return Integer.toString(behavior);
         }
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 33fc6fb..32a21c5 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -215,7 +215,7 @@
                             return true;
                         }
 
-                        // The package is now a part of the forced scoped storage whitelist
+                        // The package is now a part of the forced scoped storage allowlist
                         if (isForcedScopedStorage) {
                             return true;
                         }
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 577468b..33bed3d 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -56,7 +56,6 @@
 public final class HintManagerService extends SystemService {
     private static final String TAG = "HintManagerService";
     private static final boolean DEBUG = false;
-    private static final int MAX_HINT_SESSION_COUNT_PER_UID = 20;
     @VisibleForTesting final long mHintSessionPreferredRate;
 
     // Multi-level map storing all active AppHintSessions.
@@ -368,23 +367,6 @@
                     + " not be empty.");
 
             final int callingUid = Binder.getCallingUid();
-            if (callingUid != Process.SYSTEM_UID) {
-                int sessionCount = 0;
-                synchronized (mLock) {
-                    ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
-                            mActiveSessions.get(callingUid);
-                    if (tokenMap != null) {
-                        for (ArraySet<AppHintSession> arr : tokenMap.values()) {
-                            sessionCount += arr.size();
-                        }
-                    }
-                }
-                if (sessionCount >= MAX_HINT_SESSION_COUNT_PER_UID) {
-                    throw new IllegalStateException(
-                            "Max session count limit reached: " + sessionCount);
-                }
-            }
-
             final int callingTgid = Process.getThreadGroupLeader(Binder.getCallingPid());
             final long identity = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/security/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java
index da1ff73..caa5340 100644
--- a/services/core/java/com/android/server/security/KeyChainSystemService.java
+++ b/services/core/java/com/android/server/security/KeyChainSystemService.java
@@ -54,7 +54,7 @@
     /**
      * Maximum time limit for the KeyChain app to deal with packages being removed.
      */
-    private static final int KEYCHAIN_IDLE_WHITELIST_DURATION_MS = 30 * 1000;
+    private static final int KEYCHAIN_IDLE_ALLOWLIST_DURATION_MS = 30 * 1000;
 
     public KeyChainSystemService(final Context context) {
         super(context);
@@ -105,7 +105,7 @@
         final DeviceIdleInternal idleController =
                 LocalServices.getService(DeviceIdleInternal.class);
         idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
-                KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false,
+                KEYCHAIN_IDLE_ALLOWLIST_DURATION_MS, user.getIdentifier(), false,
                 REASON_KEY_CHAIN, "keychain");
 
         getContext().startServiceAsUser(intent, user);
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
index 2bd7383..1c5838c 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
@@ -105,14 +105,27 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
-            new RemoteProvisioningShellCommand().dump(pw);
+            final int callerUid = Binder.getCallingUidOrThrow();
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                new RemoteProvisioningShellCommand(getContext(), callerUid).dump(pw);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
         }
 
         @Override
         public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
                 ParcelFileDescriptor err, String[] args) {
-            return new RemoteProvisioningShellCommand().exec(this, in.getFileDescriptor(),
-                    out.getFileDescriptor(), err.getFileDescriptor(), args);
+            final int callerUid = Binder.getCallingUidOrThrow();
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                return new RemoteProvisioningShellCommand(getContext(), callerUid).exec(this,
+                        in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
+                        args);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
index 187b939..4a6d746 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
@@ -16,22 +16,30 @@
 
 package com.android.server.security.rkp;
 
+import android.content.Context;
 import android.hardware.security.keymint.DeviceInfo;
 import android.hardware.security.keymint.IRemotelyProvisionedComponent;
 import android.hardware.security.keymint.MacedPublicKey;
 import android.hardware.security.keymint.ProtectedData;
 import android.hardware.security.keymint.RpcHardwareInfo;
+import android.os.CancellationSignal;
+import android.os.OutcomeReceiver;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ShellCommand;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
 import android.util.IndentingPrintWriter;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.time.Duration;
 import java.util.Base64;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 
 import co.nstant.in.cbor.CborDecoder;
 import co.nstant.in.cbor.CborEncoder;
@@ -54,16 +62,17 @@
             + "csr [--challenge CHALLENGE] NAME\n"
             + "  Generate and print a base64-encoded CSR from the named\n"
             + "  IRemotelyProvisionedComponent. A base64-encoded challenge can be provided,\n"
-            + "  or else it defaults to an empty challenge.\n";
+            + "  or else it defaults to an empty challenge.\n"
+            + "certify NAME\n"
+            + "  Output the PEM-encoded certificate chain provisioned for the named\n"
+            + "  IRemotelyProvisionedComponent.\n";
 
-    @VisibleForTesting
     static final String EEK_ED25519_BASE64 = "goRDoQEnoFgqpAEBAycgBiFYIJm57t1e5FL2hcZMYtw+YatXSH11N"
             + "ymtdoAy0rPLY1jZWEAeIghLpLekyNdOAw7+uK8UTKc7b6XN3Np5xitk/pk5r3bngPpmAIUNB5gqrJFcpyUUS"
             + "QY0dcqKJ3rZ41pJ6wIDhEOhASegWE6lAQECWCDQrsEVyirPc65rzMvRlh1l6LHd10oaN7lDOpfVmd+YCAM4G"
             + "CAEIVggvoXnRsSjQlpA2TY6phXQLFh+PdwzAjLS/F4ehyVfcmBYQJvPkOIuS6vRGLEOjl0gJ0uEWP78MpB+c"
             + "gWDvNeCvvpkeC1UEEvAMb9r6B414vAtzmwvT/L1T6XUg62WovGHWAQ=";
 
-    @VisibleForTesting
     static final String EEK_P256_BASE64 = "goRDoQEmoFhNpQECAyYgASFYIPcUituX9MxT79JkEcTjdR9mH6RxDGzP"
             + "+glGgHSHVPKtIlggXn9b9uzk9hnM/xM3/Q+hyJPbGAZ2xF3m12p3hsMtr49YQC+XjkL7vgctlUeFR5NAsB/U"
             + "m0ekxESp8qEHhxDHn8sR9L+f6Dvg5zRMFfx7w34zBfTRNDztAgRgehXgedOK/ySEQ6EBJqBYcaYBAgJYIDVz"
@@ -74,14 +83,20 @@
     private static final int ERROR = -1;
     private static final int SUCCESS = 0;
 
+    private static final Duration BIND_TIMEOUT = Duration.ofSeconds(10);
+    private static final int KEY_ID = 452436;
+
+    private final Context mContext;
+    private final int mCallerUid;
     private final Injector mInjector;
 
-    RemoteProvisioningShellCommand() {
-        this(new Injector());
+    RemoteProvisioningShellCommand(Context context, int callerUid) {
+        this(context, callerUid, new Injector());
     }
 
-    @VisibleForTesting
-    RemoteProvisioningShellCommand(Injector injector) {
+    RemoteProvisioningShellCommand(Context context, int callerUid, Injector injector) {
+        mContext = context;
+        mCallerUid = callerUid;
         mInjector = injector;
     }
 
@@ -102,6 +117,8 @@
                     return list();
                 case "csr":
                     return csr();
+                case "certify":
+                    return certify();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -232,7 +249,45 @@
         return new CborDecoder(bais).decodeNext();
     }
 
-    @VisibleForTesting
+    private int certify() throws Exception {
+        String name = getNextArgRequired();
+
+        Executor executor = mContext.getMainExecutor();
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        OutcomeFuture<RemotelyProvisionedKey> key = new OutcomeFuture<>();
+        mInjector.getRegistrationProxy(mContext, mCallerUid, name, executor)
+                .getKeyAsync(KEY_ID, cancellationSignal, executor, key);
+        byte[] encodedCertChain = key.join().getEncodedCertChain();
+        ByteArrayInputStream is = new ByteArrayInputStream(encodedCertChain);
+        PrintWriter pw = getOutPrintWriter();
+        for (Certificate cert : CertificateFactory.getInstance("X.509").generateCertificates(is)) {
+            String encoded = Base64.getEncoder().encodeToString(cert.getEncoded());
+            pw.println("-----BEGIN CERTIFICATE-----");
+            pw.println(encoded.replaceAll("(.{64})", "$1\n").stripTrailing());
+            pw.println("-----END CERTIFICATE-----");
+        }
+        return SUCCESS;
+    }
+
+    /** Treat an OutcomeReceiver as a future for use in synchronous code. */
+    private static class OutcomeFuture<T> implements OutcomeReceiver<T, Exception> {
+        private CompletableFuture<T> mFuture = new CompletableFuture<>();
+
+        @Override
+          public void onResult(T result) {
+            mFuture.complete(result);
+        }
+
+        @Override
+        public void onError(Exception e) {
+            mFuture.completeExceptionally(e);
+        }
+
+        public T join() {
+            return mFuture.join();
+        }
+    }
+
     static class Injector {
         String[] getIrpcNames() {
             return ServiceManager.getDeclaredInstances(IRemotelyProvisionedComponent.DESCRIPTOR);
@@ -248,5 +303,14 @@
             }
             return binder;
         }
+
+        RegistrationProxy getRegistrationProxy(
+                Context context, int callerUid, String name, Executor executor) {
+            String irpc = IRemotelyProvisionedComponent.DESCRIPTOR + "/" + name;
+            OutcomeFuture<RegistrationProxy> registration = new OutcomeFuture<>();
+            RegistrationProxy.createAsync(
+                    context, callerUid, irpc, BIND_TIMEOUT, executor, registration);
+            return registration.join();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 64c7c6f..649ab8f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7258,8 +7258,10 @@
     }
 
     void showStartingWindow(boolean taskSwitch) {
-        showStartingWindow(null /* prev */, false /* newTask */, taskSwitch,
-                false /* startActivity */, null);
+        // Pass the activity which contains starting window already.
+        final ActivityRecord prev = task.getActivity(
+                a -> a != this && a.mStartingData != null && a.showToCurrentUser());
+        showStartingWindow(prev, false /* newTask */, taskSwitch, false /* startActivity */, null);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5c82dba..59677f4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -327,6 +327,12 @@
      */
     private SurfaceControl mOverlayLayer;
 
+    /**
+     * A SurfaceControl that contains input overlays used for cases where we need to receive input
+     * over the entire display.
+     */
+    private SurfaceControl mInputOverlayLayer;
+
     /** A surfaceControl specifically for accessibility overlays. */
     private SurfaceControl mA11yOverlayLayer;
 
@@ -1327,6 +1333,12 @@
             transaction.reparent(mOverlayLayer, mSurfaceControl);
         }
 
+        if (mInputOverlayLayer == null) {
+            mInputOverlayLayer = b.setName("Input Overlays").setParent(mSurfaceControl).build();
+        } else {
+            transaction.reparent(mInputOverlayLayer, mSurfaceControl);
+        }
+
         if (mA11yOverlayLayer == null) {
             mA11yOverlayLayer =
                     b.setName("Accessibility Overlays").setParent(mSurfaceControl).build();
@@ -1340,7 +1352,9 @@
                 .show(mSurfaceControl)
                 .setLayer(mOverlayLayer, Integer.MAX_VALUE)
                 .show(mOverlayLayer)
-                .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 1)
+                .setLayer(mInputOverlayLayer, Integer.MAX_VALUE - 1)
+                .show(mInputOverlayLayer)
+                .setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 2)
                 .show(mA11yOverlayLayer);
     }
 
@@ -3351,6 +3365,7 @@
             // -> this DisplayContent.
             setRemoteInsetsController(null);
             mOverlayLayer.release();
+            mInputOverlayLayer.release();
             mA11yOverlayLayer.release();
             mWindowingLayer.release();
             mInputMonitor.onDisplayRemoved();
@@ -5704,6 +5719,10 @@
         return mOverlayLayer;
     }
 
+    SurfaceControl getInputOverlayLayer() {
+        return mInputOverlayLayer;
+    }
+
     SurfaceControl getA11yOverlayLayer() {
         return mA11yOverlayLayer;
     }
@@ -7060,6 +7079,7 @@
         new Transaction().reparent(sc, getSurfaceControl())
                 .reparent(mWindowingLayer, null)
                 .reparent(mOverlayLayer, null)
+                .reparent(mInputOverlayLayer, null)
                 .reparent(mA11yOverlayLayer, null)
                 .apply();
     }
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 73fdfe0..8cf4713 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -275,11 +275,17 @@
                         + " - DisplayContent not found.");
                 return null;
             }
+            final SurfaceControl inputOverlay = dc.getInputOverlayLayer();
+            if (inputOverlay == null) {
+                Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+                        + " - Input overlay layer is not initialized.");
+                return null;
+            }
             return mService.makeSurfaceBuilder(dc.getSession())
                     .setContainerLayer()
                     .setName(name)
                     .setCallsite("createSurfaceForGestureMonitor")
-                    .setParent(dc.getSurfaceControl())
+                    .setParent(inputOverlay)
                     .build();
         }
     }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2f0c303..4089a0d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2277,7 +2277,7 @@
     int finishTopCrashedActivities(WindowProcessController app, String reason) {
         Task focusedRootTask = getTopDisplayFocusedRootTask();
         final Task[] finishedTask = new Task[1];
-        forAllTasks(rootTask -> {
+        forAllRootTasks(rootTask -> {
             final Task t = rootTask.finishTopCrashedActivityLocked(app, reason);
             if (rootTask == focusedRootTask || finishedTask[0] == null) {
                 finishedTask[0] = t;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 43430dd..f9bbc68 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -725,7 +725,7 @@
             } catch (RemoteException e) {
             }
         }
-        if (autoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
+        if (shouldAutoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
             // Task creator asked to remove this when done, or this task was a voice
             // interaction, so it should not remain on the recent tasks list.
             mTaskSupervisor.mRecentTasks.remove(this);
@@ -1558,12 +1558,14 @@
         return count > 0;
     }
 
-    private boolean autoRemoveFromRecents(TaskFragment oldParentFragment) {
+    private boolean shouldAutoRemoveFromRecents(TaskFragment oldParentFragment) {
         // We will automatically remove the task either if it has explicitly asked for
         // this, or it is empty and has never contained an activity that got shown to
-        // the user, or it was being embedded in another Task.
-        return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()
-                || (oldParentFragment != null && oldParentFragment.isEmbedded()));
+        // the user, or it was being embedded in another Task, or the display policy
+        // doesn't allow recents,
+        return autoRemoveRecents || (!hasChild() && !getHasBeenVisible())
+                || (oldParentFragment != null && oldParentFragment.isEmbedded())
+                || (mDisplayContent != null && !mDisplayContent.canShowTasksInHostDeviceRecents());
     }
 
     private void clearPinnedTaskIfNeed() {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 6ede345..3c85f08 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -1147,6 +1147,7 @@
             Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */);
         } else if (!animatingState && mAnimatingState) {
             t.setEarlyWakeupEnd();
+            mAtm.mWindowManager.requestTraversal();
             mSnapshotController.setPause(false);
             mAnimatingState = false;
             Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 03efb1b..439b719 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8338,12 +8338,18 @@
                             + displayId + " - DisplayContent not found.");
                     return null;
                 }
-                //TODO (b/210039666): Use a method like add/removeDisplayOverlay if available.
+                final SurfaceControl inputOverlay = dc.getInputOverlayLayer();
+                if (inputOverlay == null) {
+                    Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+                            + " - Input overlay layer is not initialized.");
+                    return null;
+                }
+                // TODO(b/210039666): Use a method like add/removeDisplayOverlay if available.
                 return makeSurfaceBuilder(dc.getSession())
                         .setContainerLayer()
                         .setName("IME Handwriting Surface")
                         .setCallsite("getHandwritingSurfaceForDisplay")
-                        .setParent(dc.getSurfaceControl())
+                        .setParent(inputOverlay)
                         .build();
             }
         }
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 7104a80..d833fbd 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -459,16 +459,26 @@
     <xs:complexType name="autoBrightness">
         <xs:attribute name="enabled" type="xs:boolean" use="optional" default="true"/>
         <xs:sequence>
-            <!-- Sets the debounce for autoBrightness brightening in millis-->
+            <!-- Sets the debounce for autoBrightness brightening in millis -->
             <xs:element name="brighteningLightDebounceMillis" type="xs:nonNegativeInteger"
                         minOccurs="0" maxOccurs="1">
                 <xs:annotation name="final"/>
             </xs:element>
-            <!-- Sets the debounce for autoBrightness darkening in millis-->
+            <!-- Sets the debounce for autoBrightness darkening in millis -->
             <xs:element name="darkeningLightDebounceMillis" type="xs:nonNegativeInteger"
                         minOccurs="0" maxOccurs="1">
                 <xs:annotation name="final"/>
             </xs:element>
+            <!-- Sets the debounce for autoBrightness brightening in millis while in idle mode -->
+            <xs:element name="brighteningLightDebounceIdleMillis" type="xs:nonNegativeInteger"
+                        minOccurs="0" maxOccurs="1">
+                <xs:annotation name="final"/>
+            </xs:element>
+            <!-- Sets the debounce for autoBrightness darkening in millis while in idle mode -->
+            <xs:element name="darkeningLightDebounceIdleMillis" type="xs:nonNegativeInteger"
+                        minOccurs="0" maxOccurs="1">
+                <xs:annotation name="final"/>
+            </xs:element>
             <!-- Sets the brightness mapping of the desired screen brightness in nits to the
              corresponding lux for the current display -->
             <xs:element name="displayBrightnessMapping" type="displayBrightnessMapping"
@@ -579,6 +589,10 @@
                     minOccurs="1" maxOccurs="1">
             <xs:annotation name="final"/>
         </xs:element>
+        <xs:element type ="xs:string" name="refreshRateThermalThrottlingId">
+            <xs:annotation name="nullable"/>
+            <xs:annotation name="final"/>
+        </xs:element>
         <xs:element name="blockingZoneThreshold" type="blockingZoneThreshold"
                     minOccurs="1" maxOccurs="1">
             <xs:annotation name="final"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 507c9dc..d2ac1aae 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -3,11 +3,15 @@
 
   public class AutoBrightness {
     ctor public AutoBrightness();
+    method public final java.math.BigInteger getBrighteningLightDebounceIdleMillis();
     method public final java.math.BigInteger getBrighteningLightDebounceMillis();
+    method public final java.math.BigInteger getDarkeningLightDebounceIdleMillis();
     method public final java.math.BigInteger getDarkeningLightDebounceMillis();
     method public final com.android.server.display.config.DisplayBrightnessMapping getDisplayBrightnessMapping();
     method public boolean getEnabled();
+    method public final void setBrighteningLightDebounceIdleMillis(java.math.BigInteger);
     method public final void setBrighteningLightDebounceMillis(java.math.BigInteger);
+    method public final void setDarkeningLightDebounceIdleMillis(java.math.BigInteger);
     method public final void setDarkeningLightDebounceMillis(java.math.BigInteger);
     method public final void setDisplayBrightnessMapping(com.android.server.display.config.DisplayBrightnessMapping);
     method public void setEnabled(boolean);
@@ -17,8 +21,10 @@
     ctor public BlockingZoneConfig();
     method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold();
     method public final java.math.BigInteger getDefaultRefreshRate();
+    method @Nullable public final String getRefreshRateThermalThrottlingId();
     method public final void setBlockingZoneThreshold(com.android.server.display.config.BlockingZoneThreshold);
     method public final void setDefaultRefreshRate(java.math.BigInteger);
+    method public final void setRefreshRateThermalThrottlingId(@Nullable String);
   }
 
   public class BlockingZoneThreshold {
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 9a19c2d..f0705ed 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -985,7 +985,7 @@
     ) {
         val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as
             AppIdAppOpPolicy
-        val appOpName = AppOpsManager.permissionToOp(permissionName)
+        val appOpName = checkNotNull(AppOpsManager.permissionToOp(permissionName))
         val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
         with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
     }
diff --git a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
index d7d2726..7858b30 100644
--- a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
+++ b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
@@ -43,7 +43,7 @@
         assertThat(packageInstallers).isNotEmpty()
 
         packageInstallers.forEach {
-            val exported = it.receivers.filter { it.exported }
+            val exported = it.receivers?.filter { it.exported }
             assertWithMessage("Receivers should not be exported").that(exported).isEmpty()
         }
     }
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index b7a0cf3..e33ca77 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -138,14 +138,6 @@
     }
 
     @Test
-    public void testGetUserMinAspectRatio_withCrossUserId() {
-        final int crossUserId = UserHandle.myUserId() + 1;
-        assertThrows(SecurityException.class,
-                () -> mIPackageManager.getUserMinAspectRatio(
-                        mInstrumentation.getContext().getPackageName(), crossUserId));
-    }
-
-    @Test
     public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
         final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
         assertThrows(IllegalArgumentException.class,
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
index 2d93120..007c0db 100644
--- a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
@@ -21,12 +21,14 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.hardware.security.keymint.DeviceInfo;
 import android.hardware.security.keymint.IRemotelyProvisionedComponent;
 import android.hardware.security.keymint.MacedPublicKey;
@@ -34,28 +36,35 @@
 import android.hardware.security.keymint.RpcHardwareInfo;
 import android.os.Binder;
 import android.os.FileUtils;
+import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
 
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.util.Arrays;
 import java.util.Base64;
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 @RunWith(AndroidJUnit4.class)
 public class RemoteProvisioningShellCommandTest {
 
+    private Context mContext;
+
     private static class Injector extends RemoteProvisioningShellCommand.Injector {
 
-        private final Map<String, IRemotelyProvisionedComponent> mIrpcs;
-
-        Injector(Map irpcs) {
-            mIrpcs = irpcs;
-        }
+        Map<String, IRemotelyProvisionedComponent> mIrpcs;
+        Map<String, RegistrationProxy> mRegistrationProxies;
 
         @Override
         String[] getIrpcNames() {
@@ -70,6 +79,12 @@
             }
             return irpc;
         }
+
+        @Override
+        RegistrationProxy getRegistrationProxy(
+                Context context, int callerUid, String name, Executor executor) {
+            return mRegistrationProxies.get(name);
+        }
     }
 
     private static class CommandResult {
@@ -111,10 +126,17 @@
                 code, FileUtils.readTextFile(out, 0, null), FileUtils.readTextFile(err, 0, null));
     }
 
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+    }
+
     @Test
     public void list_zeroInstances() throws Exception {
+        Injector injector = new Injector();
+        injector.mIrpcs = Map.of();
         RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
-                new Injector(Map.of()));
+                mContext, Process.SHELL_UID, injector);
         CommandResult res = exec(cmd, new String[] {"list"});
         assertThat(res.getErr()).isEmpty();
         assertThat(res.getCode()).isEqualTo(0);
@@ -124,8 +146,10 @@
 
     @Test
     public void list_oneInstances() throws Exception {
+        Injector injector = new Injector();
+        injector.mIrpcs = Map.of("default", mock(IRemotelyProvisionedComponent.class));
         RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
-                new Injector(Map.of("default", mock(IRemotelyProvisionedComponent.class))));
+                mContext, Process.SHELL_UID, injector);
         CommandResult res = exec(cmd, new String[] {"list"});
         assertThat(res.getErr()).isEmpty();
         assertThat(res.getCode()).isEqualTo(0);
@@ -134,10 +158,12 @@
 
     @Test
     public void list_twoInstances() throws Exception {
+        Injector injector = new Injector();
+        injector.mIrpcs = Map.of(
+                "default", mock(IRemotelyProvisionedComponent.class),
+                "strongbox", mock(IRemotelyProvisionedComponent.class));
         RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
-                new Injector(Map.of(
-                       "default", mock(IRemotelyProvisionedComponent.class),
-                       "strongbox", mock(IRemotelyProvisionedComponent.class))));
+                mContext, Process.SHELL_UID, injector);
         CommandResult res = exec(cmd, new String[] {"list"});
         assertThat(res.getErr()).isEmpty();
         assertThat(res.getCode()).isEqualTo(0);
@@ -158,8 +184,10 @@
         }).when(defaultMock).generateCertificateRequest(
                 anyBoolean(), any(), any(), any(), any(), any());
 
+        Injector injector = new Injector();
+        injector.mIrpcs = Map.of("default", defaultMock);
         RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
-                new Injector(Map.of("default", defaultMock)));
+                mContext, Process.SHELL_UID, injector);
         CommandResult res = exec(cmd, new String[] {
                 "csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
         verify(defaultMock).generateCertificateRequest(
@@ -189,8 +217,10 @@
         }).when(defaultMock).generateCertificateRequest(
                 anyBoolean(), any(), any(), any(), any(), any());
 
+        Injector injector = new Injector();
+        injector.mIrpcs = Map.of("default", defaultMock);
         RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
-                new Injector(Map.of("default", defaultMock)));
+                mContext, Process.SHELL_UID, injector);
         CommandResult res = exec(cmd, new String[] {
                 "csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
         verify(defaultMock).generateCertificateRequest(
@@ -215,8 +245,10 @@
         when(defaultMock.generateCertificateRequestV2(any(), any()))
             .thenReturn(new byte[] {0x68, 0x65, 0x6c, 0x6c, 0x6f});
 
+        Injector injector = new Injector();
+        injector.mIrpcs = Map.of("default", defaultMock);
         RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
-                new Injector(Map.of("default", defaultMock)));
+                mContext, Process.SHELL_UID, injector);
         CommandResult res = exec(cmd, new String[] {"csr", "default"});
         verify(defaultMock).generateCertificateRequestV2(new MacedPublicKey[0], new byte[0]);
         assertThat(res.getErr()).isEmpty();
@@ -233,8 +265,10 @@
         when(defaultMock.generateCertificateRequestV2(any(), any()))
             .thenReturn(new byte[] {0x68, 0x69});
 
+        Injector injector = new Injector();
+        injector.mIrpcs = Map.of("default", defaultMock);
         RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
-                new Injector(Map.of("default", defaultMock)));
+                mContext, Process.SHELL_UID, injector);
         CommandResult res = exec(cmd, new String[] {"csr", "--challenge", "dHJpYWw=", "default"});
         verify(defaultMock).generateCertificateRequestV2(
                 new MacedPublicKey[0], new byte[] {0x74, 0x72, 0x69, 0x61, 0x6c});
@@ -242,4 +276,82 @@
         assertThat(res.getCode()).isEqualTo(0);
         assertThat(res.getOut()).isEqualTo("aGk=\n");
     }
+
+    @Test
+    public void certify_sameOrderAsReceived() throws Exception {
+        String cert1 = "MIIBqDCCAU2gAwIBAgIUI3FFU7xZno/2Xf/wZzKKquP0ov0wCgYIKoZIzj0EAwIw\n"
+                + "KTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARUZXN0MB4XDTIz\n"
+                + "MDgyMjE5MzgxMFoXDTMzMDgxOTE5MzgxMFowKTELMAkGA1UEBhMCVVMxCzAJBgNV\n"
+                + "BAgMAkNBMQ0wCwYDVQQKDARUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+                + "czOpG6NKOdDjV/yrKjuy0q0jEJvsVLGgTeY+vyKRBJS59OhyRWG6n3aza21bNg5d\n"
+                + "WE9ruz+bcT0IP4kDbiS0y6NTMFEwHQYDVR0OBBYEFHYfJxCUipNI7qRqvczcWsOb\n"
+                + "FIDPMB8GA1UdIwQYMBaAFHYfJxCUipNI7qRqvczcWsObFIDPMA8GA1UdEwEB/wQF\n"
+                + "MAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKm/kpJwlnWkjoLCAddBiSnxbT4EfJIK\n"
+                + "H0j58tg5VazHAiEAnS/kRzU9AbstOZyD7el/ws3gLXkbUNey3pLFutBWsSU=\n";
+        String cert2 = "MIIBpjCCAU2gAwIBAgIUdSzfZzeGr+h70JPO7Sxwdkw99iMwCgYIKoZIzj0EAwIw\n"
+                + "KTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ0wCwYDVQQKDARUZXN0MB4XDTIz\n"
+                + "MDgyMjIwMTcyMFoXDTMzMDgxOTIwMTcyMFowKTELMAkGA1UEBhMCVVMxCzAJBgNV\n"
+                + "BAgMAkNBMQ0wCwYDVQQKDARUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+                + "voGJi4DxuqH8rzPV6Eq0OVULc0xFzaM0500VBqiQEB7Qt0Ktk2d+3bUrFAb3SZV4\n"
+                + "6TIdb7SkynvaDtr0x45Ng6NTMFEwHQYDVR0OBBYEFMeGjvGV0ADPBJk5/FPoW9HQ\n"
+                + "uTc6MB8GA1UdIwQYMBaAFMeGjvGV0ADPBJk5/FPoW9HQuTc6MA8GA1UdEwEB/wQF\n"
+                + "MAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgd1gu7iiNOQXaQUn5BT3WwWR0Yk78ndWt\n"
+                + "ew7tRiTOhFcCIFURi6WcNH0oWa6IbwBSMC9aZlo98Fbg+dTwhLAAw+PW\n";
+        byte[] cert1Bytes = Base64.getDecoder().decode(cert1.replaceAll("\\s+", ""));
+        byte[] cert2Bytes = Base64.getDecoder().decode(cert2.replaceAll("\\s+", ""));
+        byte[] certChain = Arrays.copyOf(cert1Bytes, cert1Bytes.length + cert2Bytes.length);
+        System.arraycopy(cert2Bytes, 0, certChain, cert1Bytes.length, cert2Bytes.length);
+        RemotelyProvisionedKey keyMock = mock(RemotelyProvisionedKey.class);
+        when(keyMock.getEncodedCertChain()).thenReturn(certChain);
+        RegistrationProxy defaultMock = mock(RegistrationProxy.class);
+        doAnswer(invocation -> {
+            ((OutcomeReceiver<RemotelyProvisionedKey, Exception>) invocation.getArgument(3))
+                    .onResult(keyMock);
+            return null;
+        }).when(defaultMock).getKeyAsync(anyInt(), any(), any(), any());
+
+        Injector injector = new Injector();
+        injector.mRegistrationProxies = Map.of("default", defaultMock);
+        RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+                mContext, Process.SHELL_UID, injector);
+        CommandResult res = exec(cmd, new String[] {"certify", "default"});
+        assertThat(res.getErr()).isEmpty();
+        assertThat(res.getCode()).isEqualTo(0);
+        assertThat(res.getOut()).isEqualTo(
+                "-----BEGIN CERTIFICATE-----\n" + cert1 + "-----END CERTIFICATE-----\n"
+                + "-----BEGIN CERTIFICATE-----\n" + cert2 + "-----END CERTIFICATE-----\n");
+    }
+
+    @Test
+    public void certify_noBlankLineBeforeTrailer() throws Exception {
+        String cert = "MIIB2zCCAYGgAwIBAgIRAOpN7Em1k7gaqLAB2dzXUTYwCgYIKoZIzj0EAwIwKTET\n"
+                + "MBEGA1UEChMKR29vZ2xlIExMQzESMBAGA1UEAxMJRHJvaWQgQ0EzMB4XDTIzMDgx\n"
+                + "ODIzMzI1MloXDTIzMDkyMTIzMzI1MlowOTEMMAoGA1UEChMDVEVFMSkwJwYDVQQD\n"
+                + "EyBlYTRkZWM0OWI1OTNiODFhYThiMDAxZDlkY2Q3NTEzNjBZMBMGByqGSM49AgEG\n"
+                + "CCqGSM49AwEHA0IABHM/cKZblmlw8bdGbDXnX+ZiLiGjSjaLHXYOoHDrVArAMXUi\n"
+                + "L6brhcUPaqSGcVLcfFZbaFMOxXW6TsGdQiwJ0iyjejB4MB0GA1UdDgQWBBTYzft+\n"
+                + "X32TH/Hh+ngwQF6aPhnfXDAfBgNVHSMEGDAWgBQT4JObI9mzNNW2FRsHRcw4zVn2\n"
+                + "8jAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwICBDAVBgorBgEEAdZ5AgEe\n"
+                + "BAehARoABAAAMAoGCCqGSM49BAMCA0gAMEUCIDc0OR7CzIYw0myTr0y/Brl1nZyk\n"
+                + "eGSQp615WpTwYhwxAiEApM10gSIKBIo7Z4/FNzkuiz1zZwW9+Dcqisqxkfe6icQ=\n";
+        byte[] certBytes = Base64.getDecoder().decode(cert.replaceAll("\\s+", ""));
+        RemotelyProvisionedKey keyMock = mock(RemotelyProvisionedKey.class);
+        when(keyMock.getEncodedCertChain()).thenReturn(certBytes);
+        RegistrationProxy defaultMock = mock(RegistrationProxy.class);
+        doAnswer(invocation -> {
+            ((OutcomeReceiver<RemotelyProvisionedKey, Exception>) invocation.getArgument(3))
+                    .onResult(keyMock);
+            return null;
+        }).when(defaultMock).getKeyAsync(anyInt(), any(), any(), any());
+
+        Injector injector = new Injector();
+        injector.mRegistrationProxies = Map.of("strongbox", defaultMock);
+        RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+                mContext, Process.SHELL_UID, injector);
+        CommandResult res = exec(cmd, new String[] {"certify", "strongbox"});
+        assertThat(res.getErr()).isEmpty();
+        assertThat(res.getCode()).isEqualTo(0);
+        assertThat(res.getOut()).isEqualTo(
+                "-----BEGIN CERTIFICATE-----\n" + cert + "-----END CERTIFICATE-----\n");
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index a6acd60..d16c37a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -63,8 +63,10 @@
     private static final float BRIGHTNESS_MAX_FLOAT = 1.0f;
     private static final int LIGHT_SENSOR_RATE = 20;
     private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
-    private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0;
-    private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0;
+    private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 2000;
+    private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 4000;
+    private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 1000;
+    private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE = 2000;
     private static final float DOZE_SCALE_FACTOR = 0.0f;
     private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
@@ -96,8 +98,9 @@
 
         mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
         mContext = InstrumentationRegistry.getContext();
-        mController = setupController(mLightSensor, BrightnessMappingStrategy.NO_USER_LUX,
-                BrightnessMappingStrategy.NO_USER_BRIGHTNESS);
+        setupController(BrightnessMappingStrategy.NO_USER_LUX,
+                BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ false,
+                /* useHorizon= */ true);
     }
 
     @After
@@ -109,12 +112,12 @@
         }
     }
 
-    private AutomaticBrightnessController setupController(Sensor lightSensor, float userLux,
-            float userBrightness) {
+    private void setupController(float userLux, float userBrightness, boolean applyDebounce,
+            boolean useHorizon) {
         mClock = new OffsettableClock.Stopped();
         mTestLooper = new TestLooper(mClock::now);
 
-        AutomaticBrightnessController controller = new AutomaticBrightnessController(
+        mController = new AutomaticBrightnessController(
                 new AutomaticBrightnessController.Injector() {
                     @Override
                     public Handler getBackgroundThreadHandler() {
@@ -127,16 +130,19 @@
                     }
 
                 }, // pass in test looper instead, pass in offsettable clock
-                () -> { }, mTestLooper.getLooper(), mSensorManager, lightSensor,
+                () -> { }, mTestLooper.getLooper(), mSensorManager, mLightSensor,
                 mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN_FLOAT,
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
-                INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
-                DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
+                INITIAL_LIGHT_SENSOR_RATE, applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG : 0,
+                applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG : 0,
+                applyDebounce ? BRIGHTENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
+                applyDebounce ? DARKENING_LIGHT_DEBOUNCE_CONFIG_IDLE : 0,
+                RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
                 mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
                 mContext, mBrightnessRangeController, mBrightnessThrottler,
-                mIdleBrightnessMappingStrategy, AMBIENT_LIGHT_HORIZON_SHORT,
-                AMBIENT_LIGHT_HORIZON_LONG, userLux, userBrightness
+                mIdleBrightnessMappingStrategy, useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
+                useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userBrightness
         );
 
         when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
@@ -149,12 +155,10 @@
 
         // Configure the brightness controller and grab an instance of the sensor listener,
         // through which we can deliver fake (for test) sensor values.
-        controller.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */,
                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
                 /* shouldResetShortTermModel= */ true);
-
-        return controller;
     }
 
     @Test
@@ -536,8 +540,10 @@
         // Now let's do the same for idle mode
         mController.switchToIdleMode();
         // Called once for init, and once when switching,
-        // setAmbientLux() is called twice and once in updateAutoBrightness()
-        verify(mBrightnessMappingStrategy, times(5)).isForIdleMode();
+        // setAmbientLux() is called twice and once in updateAutoBrightness(),
+        // nextAmbientLightBrighteningTransition() and nextAmbientLightDarkeningTransition() are
+        // called twice each.
+        verify(mBrightnessMappingStrategy, times(9)).isForIdleMode();
         // Called when switching.
         verify(mBrightnessMappingStrategy, times(1)).getShortTermModelTimeout();
         verify(mBrightnessMappingStrategy, times(1)).getUserBrightness();
@@ -838,7 +844,158 @@
 
         float userLux = 1000;
         float userBrightness = 0.3f;
-        setupController(mLightSensor, userLux, userBrightness);
+        setupController(userLux, userBrightness, /* applyDebounce= */ true,
+                /* useHorizon= */ false);
         verify(mBrightnessMappingStrategy).addUserDataPoint(userLux, userBrightness);
     }
+
+    @Test
+    public void testBrighteningLightDebounce() throws Exception {
+        clearInvocations(mSensorManager);
+        setupController(BrightnessMappingStrategy.NO_USER_LUX,
+                BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+                /* useHorizon= */ false);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // t = 0
+        // Initial lux
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+        assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+        // t = 1000
+        // Lux isn't steady yet
+        mClock.fastForward(1000);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+        assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+        // t = 1500
+        // Lux isn't steady yet
+        mClock.fastForward(500);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+        assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+        // t = 2500
+        // Lux is steady now
+        mClock.fastForward(1000);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+        assertEquals(1200, mController.getAmbientLux(), EPSILON);
+    }
+
+    @Test
+    public void testDarkeningLightDebounce() throws Exception {
+        clearInvocations(mSensorManager);
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(anyFloat()))
+                .thenReturn(10000f);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(anyFloat()))
+                .thenReturn(10000f);
+        setupController(BrightnessMappingStrategy.NO_USER_LUX,
+                BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+                /* useHorizon= */ false);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // t = 0
+        // Initial lux
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+        assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+        // t = 2000
+        // Lux isn't steady yet
+        mClock.fastForward(2000);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+        assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+        // t = 2500
+        // Lux isn't steady yet
+        mClock.fastForward(500);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+        assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+        // t = 4500
+        // Lux is steady now
+        mClock.fastForward(2000);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+        assertEquals(500, mController.getAmbientLux(), EPSILON);
+    }
+
+    @Test
+    public void testBrighteningLightDebounceIdle() throws Exception {
+        clearInvocations(mSensorManager);
+        setupController(BrightnessMappingStrategy.NO_USER_LUX,
+                BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+                /* useHorizon= */ false);
+
+        mController.switchToIdleMode();
+        when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // t = 0
+        // Initial lux
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+        assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+        // t = 500
+        // Lux isn't steady yet
+        mClock.fastForward(500);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+        assertEquals(500, mController.getAmbientLux(), EPSILON);
+
+        // t = 1500
+        // Lux is steady now
+        mClock.fastForward(1000);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+        assertEquals(1200, mController.getAmbientLux(), EPSILON);
+    }
+
+    @Test
+    public void testDarkeningLightDebounceIdle() throws Exception {
+        clearInvocations(mSensorManager);
+        when(mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(anyFloat()))
+                .thenReturn(10000f);
+        when(mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(anyFloat()))
+                .thenReturn(10000f);
+        setupController(BrightnessMappingStrategy.NO_USER_LUX,
+                BrightnessMappingStrategy.NO_USER_BRIGHTNESS, /* applyDebounce= */ true,
+                /* useHorizon= */ false);
+
+        mController.switchToIdleMode();
+        when(mIdleBrightnessMappingStrategy.isForIdleMode()).thenReturn(true);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // t = 0
+        // Initial lux
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 1200));
+        assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+        // t = 1000
+        // Lux isn't steady yet
+        mClock.fastForward(1000);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+        assertEquals(1200, mController.getAmbientLux(), EPSILON);
+
+        // t = 2500
+        // Lux is steady now
+        mClock.fastForward(1500);
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 500));
+        assertEquals(500, mController.getAmbientLux(), EPSILON);
+    }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
index 53d8de0c..3c3ef9a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
@@ -25,12 +25,9 @@
 import android.content.Context;
 import android.hardware.display.DisplayManagerInternal;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.LocalServices;
-
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,25 +46,13 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
         mContext = getInstrumentation().getTargetContext();
     }
 
-    @After
-    public void tearDown() {
-        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
-    }
-
     @Test
     public void testPrepareColorFadeForInvalidDisplay() {
         when(mDisplayManagerInternalMock.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(null);
-        ColorFade colorFade = new ColorFade(DISPLAY_ID);
+        ColorFade colorFade = new ColorFade(DISPLAY_ID, mDisplayManagerInternalMock);
         assertFalse(colorFade.prepare(mContext, ColorFade.MODE_FADE));
     }
-
-    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
-        LocalServices.removeServiceForTest(clazz);
-        LocalServices.addService(clazz, mock);
-    }
-
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index d9338a9..82d00a6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -112,8 +112,6 @@
         assertArrayEquals(mDisplayDeviceConfig.getBrightness(), BRIGHTNESS, ZERO_DELTA);
         assertArrayEquals(mDisplayDeviceConfig.getNits(), NITS, ZERO_DELTA);
         assertArrayEquals(mDisplayDeviceConfig.getBacklight(), BRIGHTNESS, ZERO_DELTA);
-        assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
-        assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
         assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(), new
                 float[]{0.0f, 50.0f, 80.0f}, ZERO_DELTA);
         assertArrayEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsNits(), new
@@ -413,6 +411,17 @@
 
         assertArrayEquals(new int[]{ -1, 10, 20, 30, 40 },
                 mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux());
+
+        // Low/High zone thermal maps
+        assertEquals(new SurfaceControl.RefreshRateRange(30, 40),
+                mDisplayDeviceConfig.getLowBlockingZoneThermalMap()
+                .get(Temperature.THROTTLING_CRITICAL));
+        assertEquals(new SurfaceControl.RefreshRateRange(40, 60),
+                mDisplayDeviceConfig.getHighBlockingZoneThermalMap()
+                .get(Temperature.THROTTLING_EMERGENCY));
+
+        // Todo: Add asserts for DensityMapping,
+        // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
     }
 
     @Test
@@ -540,6 +549,16 @@
         assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
     }
 
+    @Test
+    public void testLightDebounceFromDisplayConfig() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounce(), 2000);
+        assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounce(), 1000);
+        assertEquals(mDisplayDeviceConfig.getAutoBrightnessBrighteningLightDebounceIdle(), 2500);
+        assertEquals(mDisplayDeviceConfig.getAutoBrightnessDarkeningLightDebounceIdle(), 1500);
+    }
+
     private String getValidLuxThrottling() {
         return "<luxThrottling>\n"
                 + "    <brightnessLimitMap>\n"
@@ -632,6 +651,24 @@
                + "        </refreshRateRange>\n"
                + "    </refreshRateThrottlingPoint>\n"
                + "</refreshRateThrottlingMap>\n"
+               + "<refreshRateThrottlingMap id=\"thermalLow\">\n"
+               + "    <refreshRateThrottlingPoint>\n"
+               + "        <thermalStatus>critical</thermalStatus>\n"
+               + "        <refreshRateRange>\n"
+               + "            <minimum>30</minimum>\n"
+               + "            <maximum>40</maximum>\n"
+               + "        </refreshRateRange>\n"
+               + "    </refreshRateThrottlingPoint>\n"
+               + "</refreshRateThrottlingMap>\n"
+               + "<refreshRateThrottlingMap id=\"thermalHigh\">\n"
+               + "    <refreshRateThrottlingPoint>\n"
+               + "        <thermalStatus>emergency</thermalStatus>\n"
+               + "        <refreshRateRange>\n"
+               + "            <minimum>40</minimum>\n"
+               + "            <maximum>60</maximum>\n"
+               + "        </refreshRateRange>\n"
+               + "    </refreshRateThrottlingPoint>\n"
+               + "</refreshRateThrottlingMap>\n"
                + "<refreshRateThrottlingMap id=\"test\">\n"
                + "    <refreshRateThrottlingPoint>\n"
                + "        <thermalStatus>emergency</thermalStatus>\n"
@@ -704,6 +741,12 @@
                 +   "<autoBrightness>\n"
                 +       "<brighteningLightDebounceMillis>2000</brighteningLightDebounceMillis>\n"
                 +       "<darkeningLightDebounceMillis>1000</darkeningLightDebounceMillis>\n"
+                +       "<brighteningLightDebounceIdleMillis>"
+                +           "2500"
+                +       "</brighteningLightDebounceIdleMillis>\n"
+                +       "<darkeningLightDebounceIdleMillis>"
+                +           "1500"
+                +       "</darkeningLightDebounceIdleMillis>\n"
                 +       "<displayBrightnessMapping>\n"
                 +            "<displayBrightnessPoint>\n"
                 +                "<lux>50</lux>\n"
@@ -969,6 +1012,8 @@
                 +       "<defaultRefreshRateInHbmSunlight>83</defaultRefreshRateInHbmSunlight>\n"
                 +       "<lowerBlockingZoneConfigs>\n"
                 +           "<defaultRefreshRate>75</defaultRefreshRate>\n"
+                +           "<refreshRateThermalThrottlingId>thermalLow"
+                +           "</refreshRateThermalThrottlingId>\n"
                 +           "<blockingZoneThreshold>\n"
                 +               "<displayBrightnessPoint>\n"
                 +                   "<lux>50</lux>\n"
@@ -990,6 +1035,8 @@
                 +       "</lowerBlockingZoneConfigs>\n"
                 +       "<higherBlockingZoneConfigs>\n"
                 +           "<defaultRefreshRate>90</defaultRefreshRate>\n"
+                +           "<refreshRateThermalThrottlingId>thermalHigh"
+                +           "</refreshRateThermalThrottlingId>\n"
                 +           "<blockingZoneThreshold>\n"
                 +               "<displayBrightnessPoint>\n"
                 +                   "<lux>70</lux>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index 4c3d80a..4cbdd09 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -1061,6 +1061,8 @@
                 anyInt(),
                 anyLong(),
                 anyLong(),
+                anyLong(),
+                anyLong(),
                 anyBoolean(),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
@@ -1350,6 +1352,7 @@
                 int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
                 float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
                 long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+                long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
                 boolean resetAmbientLuxAfterWarmUpConfig,
                 HysteresisLevels ambientBrightnessThresholds,
                 HysteresisLevels screenBrightnessThresholds,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 01e49f2..68bbcbc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -63,8 +63,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.util.test.LocalServiceKeeperRule;
 import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
 import com.android.server.display.brightness.BrightnessEvent;
@@ -74,7 +74,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.testutils.OffsettableClock;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -140,6 +139,9 @@
                     .spyStatic(BatteryStatsService.class)
                     .build();
 
+    @Rule
+    public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
     @Before
     public void setUp() throws Exception {
         mClock = new OffsettableClock.Stopped();
@@ -153,9 +155,10 @@
         Settings.System.putFloatForUser(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0, UserHandle.USER_CURRENT);
 
-        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
-        addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
-                mCdsiMock);
+        mLocalServiceKeeperRule.overrideLocalService(
+                WindowManagerPolicy.class, mWindowManagerPolicyMock);
+        mLocalServiceKeeperRule.overrideLocalService(
+                ColorDisplayService.ColorDisplayServiceInternal.class, mCdsiMock);
 
         mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
 
@@ -167,12 +170,6 @@
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
     }
 
-    @After
-    public void tearDown() {
-        LocalServices.removeServiceForTest(WindowManagerPolicy.class);
-        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
-    }
-
     @Test
     public void testReleaseProxSuspendBlockersOnExit() throws Exception {
         when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
@@ -1071,6 +1068,8 @@
                 anyInt(),
                 anyLong(),
                 anyLong(),
+                anyLong(),
+                anyLong(),
                 anyBoolean(),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
@@ -1124,14 +1123,6 @@
         verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true);
     }
 
-    /**
-     * Creates a mock and registers it to {@link LocalServices}.
-     */
-    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
-        LocalServices.removeServiceForTest(clazz);
-        LocalServices.addService(clazz, mock);
-    }
-
     private void advanceTime(long timeMs) {
         mClock.fastForward(timeMs);
         mTestLooper.dispatchAll();
@@ -1330,6 +1321,7 @@
                 int lightSensorWarmUpTime, float brightnessMin, float brightnessMax,
                 float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
                 long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
+                long brighteningLightDebounceConfigIdle, long darkeningLightDebounceConfigIdle,
                 boolean resetAmbientLuxAfterWarmUpConfig,
                 HysteresisLevels ambientBrightnessThresholds,
                 HysteresisLevels screenBrightnessThresholds,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index b088dbf..168eefc 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -39,6 +39,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -1093,6 +1094,196 @@
     }
 
     @Test
+    public void testLockFpsForHighZoneWithThermalCondition() throws Exception {
+        // First, configure brightness zones or DMD won't register for sensor data.
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setHighDisplayBrightnessThresholds(new int[] { 200 });
+        config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
+        setPeakRefreshRate(120 /*fps*/);
+        director.getSettingsObserver().setDefaultRefreshRate(120);
+        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+        // Set the thresholds for High Zone
+        DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
+        when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(90);
+        when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
+        when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
+        when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+        when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] {});
+        when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] {});
+
+        // Set the thermal condition for refresh rate range
+        when(ddcMock.getHighBlockingZoneThermalMap()).thenReturn(
+                new SparseArray<RefreshRateRange>() {{
+                    put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
+                }}
+        );
+        director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+        director.start(sensorManager);
+
+        // Get the display listener so that we can send it new brightness events
+        ArgumentCaptor<DisplayListener> displayListenerCaptor =
+                  ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+                any(Handler.class),
+                eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                    | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+        DisplayListener displayListener = displayListenerCaptor.getValue();
+
+        // Get the sensor listener so that we can give it new light sensor events
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener sensorListener = listenerCaptor.getValue();
+
+        // Get the thermal listener so that we can give it new thermal conditions
+        ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
+                ArgumentCaptor.forClass(IThermalEventListener.class);
+        verify(mInjector, atLeastOnce()).registerThermalServiceListener(
+                thermalListenerCaptor.capture());
+        List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
+
+        setBrightness(100, 100, displayListener);
+        // Sensor reads 2000 lux,
+        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+        assertThat(vote).isNull();
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+        assertThat(vote).isNull();
+
+        // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+        // parameter to the necessary threshold
+        setBrightness(255, 255, displayListener);
+        // Sensor reads 9000 lux,
+        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+        assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+        assertThat(vote).isNotNull();
+        assertThat(vote.disableRefreshRateSwitching).isTrue();
+
+        // Set critical and check new refresh rate
+        Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
+        for (var listener : thermalListeners) {
+            listener.notifyThrottling(temp);
+        }
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+        assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+        assertThat(vote).isNotNull();
+        assertThat(vote.disableRefreshRateSwitching).isTrue();
+    }
+
+    @Test
+    public void testLockFpsForLowZoneWithThermalCondition() throws Exception {
+        // First, configure brightness zones or DMD won't register for sensor data.
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setHighDisplayBrightnessThresholds(new int[] { 200 });
+        config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0);
+        setPeakRefreshRate(120 /*fps*/);
+        director.getSettingsObserver().setDefaultRefreshRate(120);
+        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+        // Set the thresholds for Low Zone
+        DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
+        when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+        when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 });
+        when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 });
+        when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90);
+        when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] { 10 });
+        when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] { 10 });
+
+        // Set the thermal condition for refresh rate range
+        when(ddcMock.getLowBlockingZoneThermalMap()).thenReturn(
+                new SparseArray<RefreshRateRange>() {{
+                    put(Temperature.THROTTLING_CRITICAL, new RefreshRateRange(60, 60));
+                }}
+        );
+        director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+        director.start(sensorManager);
+
+        // Get the display listener so that we can send it new brightness events
+        ArgumentCaptor<DisplayListener> displayListenerCaptor =
+                  ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+                any(Handler.class),
+                eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                    | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+        DisplayListener displayListener = displayListenerCaptor.getValue();
+
+        // Get the sensor listener so that we can give it new light sensor events
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener sensorListener = listenerCaptor.getValue();
+
+        // Get the thermal listener so that we can give it new thermal conditions
+        ArgumentCaptor<IThermalEventListener> thermalListenerCaptor =
+                ArgumentCaptor.forClass(IThermalEventListener.class);
+        verify(mInjector, atLeastOnce()).registerThermalServiceListener(
+                thermalListenerCaptor.capture());
+        List<IThermalEventListener> thermalListeners = thermalListenerCaptor.getAllValues();
+
+        setBrightness(100, 100, displayListener);
+        // Sensor reads 2000 lux,
+        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+        assertThat(vote).isNull();
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+        assertThat(vote).isNull();
+
+        // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+        // parameter to the necessary threshold
+        setBrightness(5, 5, displayListener);
+        // Sensor reads 9 lux,
+        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+        assertVoteForPhysicalRefreshRate(vote, 90 /*fps*/);
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+        assertThat(vote).isNotNull();
+        assertThat(vote.disableRefreshRateSwitching).isTrue();
+
+        // Set critical and check new refresh rate
+        Temperature temp = getSkinTemp(Temperature.THROTTLING_CRITICAL);
+        for (var listener : thermalListeners) {
+            listener.notifyThrottling(temp);
+        }
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
+        assertVoteForPhysicalRefreshRate(vote, 60 /*fps*/);
+        vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
+        assertThat(vote).isNotNull();
+        assertThat(vote.disableRefreshRateSwitching).isTrue();
+    }
+
+    @Test
     public void testSensorRegistration() {
         // First, configure brightness zones or DMD won't register for sensor data.
         final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -2907,6 +3098,10 @@
         }
 
         @Override
+        public void unregisterThermalServiceListener(IThermalEventListener listener) {
+        }
+
+        @Override
         public boolean supportsFrameRateOverride() {
             return true;
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index 183a84d..f9fbf1b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -38,7 +38,7 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.R;
-import com.android.server.LocalServices;
+import com.android.internal.util.test.LocalServiceKeeperRule;
 import com.android.server.display.TestUtils;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.display.utils.AmbientFilter;
@@ -47,6 +47,7 @@
 import com.google.common.collect.ImmutableList;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -90,6 +91,9 @@
     @Mock private TypedArray mStrongDisplayColorTemperatures;
     @Mock private ColorDisplayService.ColorDisplayServiceInternal mColorDisplayServiceInternalMock;
 
+    @Rule
+    public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -156,8 +160,9 @@
                 R.array.config_displayWhiteBalanceHighLightAmbientBiasesStrong))
                 .thenReturn(mHighLightBiasesStrong);
         mockThrottler();
-        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
-        LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+
+        mLocalServiceKeeperRule.overrideLocalService(
+                ColorDisplayService.ColorDisplayServiceInternal.class,
                 mColorDisplayServiceInternalMock);
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index cffd027..6c39275 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -131,7 +131,7 @@
         wheneverStatic { HexEncoding.decode(STATIC_LIB_NAME, false) }
                 .thenReturn(PackageUtils.computeSha256DigestBytes(
                         mSettings.getPackageLPr(STATIC_LIB_PACKAGE_NAME)
-                            .pkg.signingDetails.signatures!![0].toByteArray()))
+                            .pkg!!.signingDetails.signatures!![0].toByteArray()))
     }
 
     @Test
@@ -239,7 +239,7 @@
         testPackageSetting.setPkgStateLibraryFiles(listOf())
         assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
 
-        mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg, testPackageSetting,
+        mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg!!, testPackageSetting,
                 null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
 
         assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
@@ -252,7 +252,7 @@
         testPackageSetting.setPkgStateLibraryFiles(listOf())
         assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
 
-        mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg, testPackageSetting,
+        mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg!!, testPackageSetting,
                 null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
 
         assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
@@ -266,7 +266,7 @@
         testPackageSetting.setPkgStateLibraryFiles(listOf())
         assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
 
-        mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg, testPackageSetting,
+        mSharedLibrariesImpl.updateSharedLibraries(testPackageSetting.pkg!!, testPackageSetting,
                 null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
 
         assertThat(testPackageSetting.usesLibraryFiles).hasSize(3)
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index 746fb53..64e776e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.biometrics;
 
+import static com.android.server.biometrics.AuthenticationStatsCollector.MAXIMUM_ENROLLMENT_NOTIFICATIONS;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -44,9 +46,11 @@
 import com.android.server.biometrics.sensors.BiometricNotification;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.io.File;
 
@@ -54,6 +58,9 @@
 @SmallTest
 public class AuthenticationStatsCollectorTest {
 
+    @Rule
+    public MockitoRule mockitoRule = MockitoJUnit.rule();
+
     private AuthenticationStatsCollector mAuthenticationStatsCollector;
     private static final float FRR_THRESHOLD = 0.2f;
     private static final int USER_ID_1 = 1;
@@ -75,7 +82,6 @@
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
 
         when(mContext.getResources()).thenReturn(mResources);
         when(mResources.getFraction(eq(R.fraction.config_biometricNotificationFrrThreshold),
@@ -130,6 +136,33 @@
         assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
     }
 
+    /**
+     * Our current use case does not need the counters to update after the notification
+     * limit is reached. If we need these counters to continue counting in the future,
+     * a separate privacy review must be done.
+     */
+    @Test
+    public void authenticate_notificationExceeded_mapMustNotBeUpdated() {
+
+        mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1,
+                new AuthenticationStats(USER_ID_1, 400 /* totalAttempts */,
+                        40 /* rejectedAttempts */,
+                        MAXIMUM_ENROLLMENT_NOTIFICATIONS /* enrollmentNotifications */,
+                        0 /* modality */));
+
+        mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
+
+        AuthenticationStats authenticationStats =
+                mAuthenticationStatsCollector.getAuthenticationStatsForUser(USER_ID_1);
+
+        assertThat(authenticationStats.getUserId()).isEqualTo(USER_ID_1);
+        // Assert that counters haven't been updated.
+        assertThat(authenticationStats.getTotalAttempts()).isEqualTo(400);
+        assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(40);
+        assertThat(authenticationStats.getEnrollmentNotifications())
+                .isEqualTo(MAXIMUM_ENROLLMENT_NOTIFICATIONS);
+    }
+
     @Test
     public void authenticate_frrNotExceeded_notificationNotExceeded_shouldNotSendNotification() {
 
@@ -156,7 +189,8 @@
 
         mAuthenticationStatsCollector.setAuthenticationStatsForUser(USER_ID_1,
                 new AuthenticationStats(USER_ID_1, 500 /* totalAttempts */,
-                        400 /* rejectedAttempts */, 2 /* enrollmentNotifications */,
+                        400 /* rejectedAttempts */,
+                        MAXIMUM_ENROLLMENT_NOTIFICATIONS /* enrollmentNotifications */,
                         0 /* modality */));
 
         mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
@@ -164,12 +198,12 @@
         // Assert that no notification should be sent.
         verify(mBiometricNotification, never()).sendFaceEnrollNotification(any());
         verify(mBiometricNotification, never()).sendFpEnrollNotification(any());
-        // Assert that data has been reset.
+        // Assert that data hasn't been reset.
         AuthenticationStats authenticationStats = mAuthenticationStatsCollector
                 .getAuthenticationStatsForUser(USER_ID_1);
-        assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
-        assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
-        assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
+        assertThat(authenticationStats.getTotalAttempts()).isEqualTo(500);
+        assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(400);
+        assertThat(authenticationStats.getFrr()).isWithin(0f).of(0.8f);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 908afc8..00e35ec 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -226,6 +226,7 @@
     private VirtualDeviceManagerService mVdms;
     private VirtualDeviceManagerInternal mLocalService;
     private VirtualDeviceManagerService.VirtualDeviceManagerImpl mVdm;
+    private VirtualDeviceLog mVirtualDeviceLog;
     @Mock
     private InputController.NativeWrapper mNativeWrapperMock;
     @Mock
@@ -370,6 +371,7 @@
         mVdms = new VirtualDeviceManagerService(mContext);
         mLocalService = mVdms.getLocalServiceInstance();
         mVdm = mVdms.new VirtualDeviceManagerImpl();
+        mVirtualDeviceLog = new VirtualDeviceLog(mContext);
         mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1);
         mSensorController = mDeviceImpl.getSensorControllerForTest();
     }
@@ -1730,7 +1732,7 @@
     private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
             VirtualDeviceParams params) {
         VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
-                mAssociationInfo, mVdms, new Binder(),
+                mAssociationInfo, mVdms, mVirtualDeviceLog, new Binder(),
                 new AttributionSource(ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
                 virtualDeviceId,
                 mInputController, mCameraAccessController,
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index ee3ab97..9fca513 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -149,32 +149,6 @@
     }
 
     @Test
-    public void testCreateHintSession_exceedsLimit() throws Exception {
-        HintManagerService service = createService();
-        IBinder token1 = new Binder();
-        IBinder token2 = new Binder();
-
-        for (int i = 0; i < 10; i++) {
-            IHintSession a = service.getBinderServiceInstance().createHintSession(token1,
-                    SESSION_TIDS_A, DEFAULT_TARGET_DURATION);
-            assertNotNull(a);
-        }
-
-        for (int i = 0; i < 10; i++) {
-            IHintSession b = service.getBinderServiceInstance().createHintSession(token2,
-                    SESSION_TIDS_B, DEFAULT_TARGET_DURATION);
-            assertNotNull(b);
-        }
-
-        assertThrows(IllegalStateException.class,
-                () -> service.getBinderServiceInstance().createHintSession(token1, SESSION_TIDS_A,
-                        DEFAULT_TARGET_DURATION));
-        assertThrows(IllegalStateException.class,
-                () -> service.getBinderServiceInstance().createHintSession(token2, SESSION_TIDS_B,
-                        DEFAULT_TARGET_DURATION));
-    }
-
-    @Test
     public void testPauseResumeHintSession() throws Exception {
         HintManagerService service = createService();
         IBinder token = new Binder();
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 579bbc8..37f4983 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6045,31 +6045,33 @@
 
     @Test
     public void testVisitUris_styleExtrasWithoutStyle() {
-        Notification notification = new Notification.Builder(mContext, "a")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .build();
+        Notification.Builder notification = new Notification.Builder(mContext, "a")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(
-                personWithIcon("content://user"))
-                .addHistoricMessage(new Notification.MessagingStyle.Message("Heyhey!",
-                                System.currentTimeMillis(),
-                                personWithIcon("content://historicalMessenger")))
-                .addMessage(new Notification.MessagingStyle.Message("Are you there",
-                                System.currentTimeMillis(),
-                                personWithIcon("content://messenger")))
-                        .setShortcutIcon(
-                                Icon.createWithContentUri("content://conversationShortcut"));
-        messagingStyle.addExtras(notification.extras); // Instead of Builder.setStyle(style).
+        Bundle messagingExtras = new Bundle();
+        messagingExtras.putParcelable(Notification.EXTRA_MESSAGING_PERSON,
+                personWithIcon("content://user"));
+        messagingExtras.putParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES,
+                new Bundle[] { new Notification.MessagingStyle.Message("Heyhey!",
+                        System.currentTimeMillis() - 100,
+                        personWithIcon("content://historicalMessenger")).toBundle()});
+        messagingExtras.putParcelableArray(Notification.EXTRA_MESSAGES,
+                new Bundle[] { new Notification.MessagingStyle.Message("Are you there?",
+                        System.currentTimeMillis(),
+                        personWithIcon("content://messenger")).toBundle()});
+        messagingExtras.putParcelable(Notification.EXTRA_CONVERSATION_ICON,
+                Icon.createWithContentUri("content://conversationShortcut"));
+        notification.addExtras(messagingExtras);
 
-        Notification.CallStyle callStyle = Notification.CallStyle.forOngoingCall(
-                        personWithIcon("content://caller"),
-                        PendingIntent.getActivity(mContext, 0, new Intent(),
-                                PendingIntent.FLAG_IMMUTABLE))
-                .setVerificationIcon(Icon.createWithContentUri("content://callVerification"));
-        callStyle.addExtras(notification.extras); // Same.
+        Bundle callExtras = new Bundle();
+        callExtras.putParcelable(Notification.EXTRA_CALL_PERSON,
+                personWithIcon("content://caller"));
+        callExtras.putParcelable(Notification.EXTRA_VERIFICATION_ICON,
+                Icon.createWithContentUri("content://callVerification"));
+        notification.addExtras(callExtras);
 
         Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
-        notification.visitUris(visitor);
+        notification.build().visitUris(visitor);
 
         verify(visitor).accept(eq(Uri.parse("content://user")));
         verify(visitor).accept(eq(Uri.parse("content://historicalMessenger")));
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 81d939f..9b745f5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -115,6 +115,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
+import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -209,6 +210,7 @@
     @Mock Context mContext;
     @Mock ZenModeHelper mMockZenModeHelper;
     @Mock AppOpsManager mAppOpsManager;
+    @Mock ManagedServices.UserProfiles mUserProfiles;
     @Mock PermissionManager mPermissionManager;
 
     private NotificationManager.Policy mTestNotificationPolicy;
@@ -327,10 +329,12 @@
         when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
                 .thenReturn(appPermissions);
 
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
+
         mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
 
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager,
+                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mStatsEventBuilderFactory, false);
         resetZenModeHelper();
 
@@ -678,7 +682,8 @@
     @Test
     public void testReadXml_oldXml_migrates() throws Exception {
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+                mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
 
         String xml = "<ranking version=\"2\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -743,9 +748,6 @@
 
     @Test
     public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
         when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
         when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
         when(mPm.getPackageUidAsUser("pkg3", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
@@ -822,7 +824,8 @@
     @Test
     public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+                mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
 
         String xml = "<ranking version=\"3\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -879,7 +882,8 @@
     @Test
     public void testReadXml_newXml_permissionNotificationOff() throws Exception {
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+                mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ false);
 
         String xml = "<ranking version=\"3\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -936,7 +940,8 @@
     @Test
     public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+                mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
 
         String xml = "<ranking version=\"4\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -991,9 +996,6 @@
 
     @Test
     public void testReadXml_oldXml_migration_NoUid() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
         when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
         String xml = "<ranking version=\"2\">\n"
                 + "<package name=\"something\" show_badge=\"true\">\n"
@@ -1024,9 +1026,6 @@
 
     @Test
     public void testReadXml_newXml_noMigration_NoUid() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
         when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
         String xml = "<ranking version=\"3\">\n"
                 + "<package name=\"something\" show_badge=\"true\">\n"
@@ -1056,9 +1055,6 @@
 
     @Test
     public void testChannelXmlForNonBackup_postMigration() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
         appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
         appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1142,9 +1138,6 @@
 
     @Test
     public void testChannelXmlForBackup_postMigration() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
         appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
         appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1234,9 +1227,6 @@
 
     @Test
     public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
         appPermissions.put(new Pair<>(UID_P, PKG_P), new Pair<>(true, false));
         appPermissions.put(new Pair<>(UID_O, PKG_O), new Pair<>(false, false));
@@ -1319,9 +1309,6 @@
 
     @Test
     public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
-
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
         appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
         appPermissions.put(new Pair<>(3, "third"), new Pair<>(false, false));
@@ -1533,7 +1520,8 @@
                 new FileNotFoundException("")).thenReturn(resId);
 
         mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+                mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
+                mStatsEventBuilderFactory, false);
 
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -2477,9 +2465,6 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         mHelper.syncChannelsBypassingDnd();
 
         // create notification channel that can bypass dnd, but app is blocked
@@ -2508,9 +2493,6 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         mHelper.syncChannelsBypassingDnd();
 
         // create notification channel that can bypass dnd, but app is blocked
@@ -2533,9 +2515,6 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         mHelper.syncChannelsBypassingDnd();
 
         // create notification channel that can bypass dnd, but app is blocked
@@ -2588,9 +2567,6 @@
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         mHelper.syncChannelsBypassingDnd();
         assertFalse(mHelper.areChannelsBypassingDnd());
         verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
@@ -2602,15 +2578,58 @@
         // start notification policy off with mAreChannelsBypassingDnd = false
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         assertFalse(mHelper.areChannelsBypassingDnd());
         verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
         resetZenModeHelper();
     }
 
     @Test
+    public void syncChannelsBypassingDnd_includesProfilesOfCurrentUser() throws Exception {
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0, 10}));
+        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+        when(mPm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(appInfo);
+
+        NotificationChannel withBypass = new NotificationChannel("1", "with", IMPORTANCE_DEFAULT);
+        withBypass.setBypassDnd(true);
+        NotificationChannel withoutBypass = new NotificationChannel("2", "without",
+                IMPORTANCE_DEFAULT);
+        withoutBypass.setBypassDnd(false);
+        mHelper.createNotificationChannel("com.example", UserHandle.getUid(0, 444), withoutBypass,
+                false, false, Process.SYSTEM_UID, true);
+        mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
+                false, false, Process.SYSTEM_UID, true);
+
+        mHelper.syncChannelsBypassingDnd();
+
+        assertThat(mHelper.areChannelsBypassingDnd()).isTrue();
+    }
+
+    @Test
+    public void syncChannelsBypassingDnd_excludesOtherUsers() throws Exception {
+        when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
+        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(true);
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+        when(mPm.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenReturn(appInfo);
+
+        NotificationChannel withBypass = new NotificationChannel("1", "with", IMPORTANCE_DEFAULT);
+        withBypass.setBypassDnd(true);
+        NotificationChannel withoutBypass = new NotificationChannel("2", "without",
+                IMPORTANCE_DEFAULT);
+        withoutBypass.setBypassDnd(false);
+        mHelper.createNotificationChannel("com.example", UserHandle.getUid(0, 444), withoutBypass,
+                false, false, Process.SYSTEM_UID, true);
+        mHelper.createNotificationChannel("com.example", UserHandle.getUid(10, 444), withBypass,
+                false, false, Process.SYSTEM_UID, true);
+
+        mHelper.syncChannelsBypassingDnd();
+
+        assertThat(mHelper.areChannelsBypassingDnd()).isFalse();
+    }
+
+    @Test
     public void testCreateDeletedChannel() throws Exception {
         long[] vibration = new long[]{100, 67, 145, 156};
         NotificationChannel channel =
@@ -3705,9 +3724,7 @@
                 + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
                 + "</package>\n"
                 + "</ranking>\n";
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
+
         loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM);
 
         assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3719,9 +3736,6 @@
         mHelper.setHideSilentStatusIcons(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS,
@@ -3789,9 +3803,6 @@
         mHelper.canShowBadge(PKG_O, UID_O);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3802,9 +3813,6 @@
         mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3816,9 +3824,6 @@
         mHelper.revokeNotificationDelegate(PKG_O, UID_O);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -3832,9 +3837,6 @@
         assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3888,9 +3890,6 @@
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O));
@@ -3926,9 +3925,6 @@
                 mHelper.getAppLockedFields(PKG_O, UID_O));
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE);
@@ -4584,10 +4580,6 @@
 
     @Test
     public void testPlaceholderConversationId_shortcutRequired() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
@@ -4604,10 +4596,6 @@
 
     @Test
     public void testNormalConversationId_shortcutRequired() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>"
@@ -4624,10 +4612,6 @@
 
     @Test
     public void testNoConversationId_shortcutRequired() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>"
@@ -4644,10 +4628,6 @@
 
     @Test
     public void testDeleted_noTime() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         final String xml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\" deleted=\"true\"/>"
@@ -4664,13 +4644,10 @@
 
     @Test
     public void testDeleted_twice() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         mHelper.createNotificationChannel(
                 PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
                 UID_P, false);
+
         assertTrue(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id",
                 UID_P, false));
         assertFalse(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id",
@@ -4679,10 +4656,6 @@
 
     @Test
     public void testDeleted_recentTime() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         mHelper.createNotificationChannel(
                 PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
                 UID_P, false);
@@ -4698,9 +4671,6 @@
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
                 null);
         parser.nextTag();
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
         mHelper.readXml(parser, true, USER_SYSTEM);
 
         NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true);
@@ -4710,10 +4680,6 @@
 
     @Test
     public void testUnDelete_time() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         mHelper.createNotificationChannel(
                 PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false,
                 UID_P, false);
@@ -4732,10 +4698,6 @@
 
     @Test
     public void testDeleted_longTime() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mPermissionManager, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory, false);
-
         long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
 
         final String xml = "<ranking version=\"1\">\n"
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index 6f65406..05a1482 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -112,7 +112,7 @@
         // Show assistant.
         mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_ASSISTANT);
         sendKey(KEYCODE_POWER, true);
-        mPhoneWindowManager.assertAssistLaunch();
+        mPhoneWindowManager.assertSearchManagerLaunchAssist();
 
         // Show global actions.
         mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_GLOBAL_ACTIONS);
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index c433e64..eab8757 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -16,11 +16,15 @@
 
 package com.android.server.policy;
 
+import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS;
 import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS;
 import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
 
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT;
 import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
+import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY;
 
+import android.content.ComponentName;
 import android.provider.Settings;
 
 import org.junit.Test;
@@ -32,6 +36,9 @@
  * atest WmTests:StemKeyGestureTests
  */
 public class StemKeyGestureTests extends ShortcutKeyTestBase {
+
+    private static final String TEST_TARGET_ACTIVITY = "com.android.server.policy/.TestActivity";
+
     /**
      * Stem single key should not launch behavior during set up.
      */
@@ -63,6 +70,57 @@
         mPhoneWindowManager.assertOpenAllAppView();
     }
 
+    /**
+     * Stem single key should not launch behavior during set up.
+     */
+    @Test
+    public void stemSingleKey_launchTargetActivity() {
+        overrideBehavior(
+                STEM_PRIMARY_BUTTON_SHORT_PRESS,
+                SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY);
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+        mPhoneWindowManager.overrideStartActivity();
+        mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+        mPhoneWindowManager.overrideIsUserSetupComplete(true);
+        mPhoneWindowManager.assumeResolveActivityNotNull();
+
+        ComponentName targetComponent = ComponentName.unflattenFromString(TEST_TARGET_ACTIVITY);
+        mPhoneWindowManager.overrideStemPressTargetActivity(targetComponent);
+
+        sendKey(KEYCODE_STEM_PRIMARY);
+
+        mPhoneWindowManager.assertActivityTargetLaunched(targetComponent);
+    }
+
+    @Test
+    public void stemLongKey_triggerSearchServiceToLaunchAssist() {
+        overrideBehavior(
+                STEM_PRIMARY_BUTTON_LONG_PRESS,
+                LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+        mPhoneWindowManager.setupAssistForLaunch();
+        mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+        sendKey(KEYCODE_STEM_PRIMARY, /* longPress= */ true);
+        mPhoneWindowManager.assertSearchManagerLaunchAssist();
+    }
+
+    @Test
+    public void stemLongKey_whenNoSearchService_triggerStatusBarToStartAssist() {
+        overrideBehavior(
+                STEM_PRIMARY_BUTTON_LONG_PRESS,
+                LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT);
+        setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+        mPhoneWindowManager.setupAssistForLaunch();
+        mPhoneWindowManager.overrideSearchManager(null);
+        mPhoneWindowManager.overrideStatusBarManagerInternal();
+        mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+        sendKey(KEYCODE_STEM_PRIMARY, /* longPress= */ true);
+        mPhoneWindowManager.assertStatusBarStartAssist();
+    }
+
+
     private void overrideBehavior(String key, int expectedBehavior) {
         Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
     }
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index ef3a6ed..bc8f06a 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -59,9 +59,11 @@
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.app.SearchManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.hardware.SensorPrivacyManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
@@ -230,6 +232,7 @@
         doReturn(mPackageManager).when(mContext).getPackageManager();
         doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
                 eq(SensorPrivacyManager.class));
+        doReturn(mSearchManager).when(mContext).getSystemService(eq(SearchManager.class));
         doReturn(false).when(mPackageManager).hasSystemFeature(any());
         try {
             doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
@@ -355,11 +358,7 @@
             case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
                 break;
             case LONG_PRESS_POWER_ASSISTANT:
-                doNothing().when(mPhoneWindowManager).sendCloseSystemWindows();
-                doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
-                doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
-                doReturn(mSearchManager).when(mContext)
-                        .getSystemService(eq(Context.SEARCH_SERVICE));
+                setupAssistForLaunch();
                 mPhoneWindowManager.mLongPressOnPowerAssistantTimeoutMs = 500;
                 break;
         }
@@ -433,6 +432,22 @@
         doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing();
     }
 
+    void setupAssistForLaunch() {
+        doNothing().when(mPhoneWindowManager).sendCloseSystemWindows();
+        doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
+        doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+    }
+
+    void overrideSearchManager(SearchManager searchManager) {
+        mPhoneWindowManager.mSearchManager = searchManager;
+    }
+
+    void assumeResolveActivityNotNull() {
+        ResolveInfo resolveInfo = new ResolveInfo();
+        doReturn(resolveInfo).when(mPackageManager).resolveActivity(any(), anyInt());
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+    }
+
     void overrideKeyEventSource(int vendorId, int productId) {
         InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId(
                 productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType(
@@ -458,6 +473,10 @@
         doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
     }
 
+    void overrideStemPressTargetActivity(ComponentName component) {
+        mPhoneWindowManager.mPrimaryShortPressTargetActivity = component;
+    }
+
     /**
      * Below functions will check the policy behavior could be invoked.
      */
@@ -517,7 +536,7 @@
                 .interceptPowerKeyDown(any(), anyBoolean(), any());
     }
 
-    void assertAssistLaunch() {
+    void assertSearchManagerLaunchAssist() {
         waitForIdle();
         verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any());
     }
@@ -540,6 +559,11 @@
         verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
     }
 
+    void assertStatusBarStartAssist() {
+        waitForIdle();
+        verify(mStatusBarManagerInternal).startAssist(any());
+    }
+
     void assertSwitchKeyboardLayout(int direction) {
         waitForIdle();
         if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) {
@@ -613,6 +637,14 @@
                 .startActivityAsUser(any(Intent.class), any(), any(UserHandle.class));
     }
 
+    void assertActivityTargetLaunched(ComponentName targetActivity) {
+        waitForIdle();
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS))
+                .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class));
+        Assert.assertEquals(targetActivity, intentCaptor.getValue().getComponent());
+    }
+
     void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent,
             int expectedKey, int expectedModifierState, String errorMsg) {
         waitForIdle();
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index befc4a0..4c978ad 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -259,7 +259,24 @@
         // NOTE: No permissions required
 
         if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
-            return FileUtils.roundStorageSize(mStorage.getPrimaryStorageSize());
+            // As a safety measure, use the original implementation for the devices
+            // with storage size <= 512GB to prevent any potential regressions
+            final long roundedUserspaceBytes = mStorage.getPrimaryStorageSize();
+            if (roundedUserspaceBytes <= DataUnit.GIGABYTES.toBytes(512)) {
+                return roundedUserspaceBytes;
+            }
+
+            // Since 1TB devices can actually have either 1000GB or 1024GB,
+            // get the block device size and do just a small rounding if any at all
+            final long totalBytes = mStorage.getInternalStorageBlockDeviceSize();
+            final long totalBytesRounded = FileUtils.roundStorageSize(totalBytes);
+            // If the storage size is 997GB-999GB, round it to a 1000GB to show
+            // 1TB in UI instead of 0.99TB. Same for 2TB, 4TB, 8TB etc.
+            if (totalBytesRounded - totalBytes <= DataUnit.GIGABYTES.toBytes(3)) {
+                return totalBytesRounded;
+            } else {
+                return totalBytes;
+            }
         } else {
             final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
             if (vol == null) {
@@ -286,15 +303,19 @@
             // Free space is usable bytes plus any cached data that we're
             // willing to automatically clear. To avoid user confusion, this
             // logic should be kept in sync with getAllocatableBytes().
+            long freeBytes;
             if (isQuotaSupported(volumeUuid, PLATFORM_PACKAGE_NAME)) {
                 final long cacheTotal = getCacheBytes(volumeUuid, PLATFORM_PACKAGE_NAME);
                 final long cacheReserved = mStorage.getStorageCacheBytes(path, 0);
                 final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
 
-                return path.getUsableSpace() + cacheClearable;
+                freeBytes = path.getUsableSpace() + cacheClearable;
             } else {
-                return path.getUsableSpace();
+                freeBytes = path.getUsableSpace();
             }
+
+            Slog.d(TAG, "getFreeBytes: " + freeBytes);
+            return freeBytes;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index a9cdf7e..94d4d22 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -791,7 +791,7 @@
                         AppOpsManager.MODE_ALLOWED);
             }
         } catch (NameNotFoundException e) {
-            // No whitelisted system app on this device
+            // No allowlisted system app on this device
             Log.e(LOG_TAG, "Package not found: " + packageName);
         }
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4907134..13e5ff1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10408,7 +10408,7 @@
         sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
                 new int[] {4 /* BUSY */});
         sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
-        sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
+        sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 5000);
         sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
                 CellSignalStrengthLte.USE_RSRP);
@@ -10518,7 +10518,7 @@
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
                 "eHRPD", new int[]{10, 400, 600, 800, 1000});
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
-                "TD_SCDMA", new int[]{1, 100, 500, 1000});
+                "TD_SCDMA", new int[]{1, 50, 100, 500, 1000});
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
                 "iDEN", new int[]{1, 2, 10, 50, 100});
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
@@ -10536,7 +10536,7 @@
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
                 "EvDo_0", new int[]{300, 600, 1000, 1500, 2000});
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
-                "1xRTT", new int[]{50, 60, 70, 80});
+                "1xRTT", new int[]{50, 60, 70, 80, 90});
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
                 "EDGE", new int[]{154, 169, 183, 192, 267});
         auto_data_switch_rat_signal_score_string_bundle.putIntArray(
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 40488b1..145cc2c 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -441,7 +441,7 @@
     }
 
     /**
-     * Get {@link Context#getOpPackageName()} if this manager has a context, otherwise a dummy
+     * Get {@link Context#getOpPackageName()} if this manager has a context, otherwise a placeholder
      * value.
      *
      * @return The package name to be used for app-ops checks
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5f67441..69b1d63 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7857,7 +7857,7 @@
     }
 
     /**
-     * Rollback modem configurations to factory default except some config which are in whitelist.
+     * Rollback modem configurations to factory default except some config which are in allowlist.
      * Used for device configuration by some carriers.
      *
      * <p>Requires Permission:
@@ -15351,7 +15351,7 @@
      *
      *  1) User data is turned on, or
      *  2) APN is un-metered for this subscription, or
-     *  3) APN type is whitelisted. E.g. MMS is whitelisted if
+     *  3) APN type is allowlisted. E.g. MMS is allowlisted if
      *  {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled.
      *
      * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index dda021e..67acda0 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -460,7 +460,7 @@
      */
     public static final int CODE_LOW_BATTERY = 505;
     /**
-     * Device declined a call due to a blacklisted caller ID.
+     * Device declined a call due to a denylisted caller ID.
      */
     public static final int CODE_BLACKLISTED_CALL_ID = 506;
     // IMS -> Telephony
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index c498216..9994002a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -57,7 +57,7 @@
  */
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SATELLITE)
 @SystemApi
-public class SatelliteManager {
+public final class SatelliteManager {
     private static final String TAG = "SatelliteManager";
 
     private static final ConcurrentHashMap<SatelliteDatagramCallback, ISatelliteDatagramCallback>
@@ -1545,11 +1545,11 @@
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
 
-    public void onDeviceAlignedWithSatellite(boolean isAligned) {
+    public void setDeviceAlignedWithSatellite(boolean isAligned) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.onDeviceAlignedWithSatellite(mSubId, isAligned);
+                telephony.setDeviceAlignedWithSatellite(mSubId, isAligned);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0c3991d..06071fe 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2978,7 +2978,7 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void onDeviceAlignedWithSatellite(int subId, in boolean isAligned);
+    void setDeviceAlignedWithSatellite(int subId, in boolean isAligned);
 
     /**
      * This API can be used by only CTS to update satellite vendor service package name.
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 1a58f17..fa452dd 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -674,7 +674,7 @@
             return true;
         }
 
-        // iorap compiler filters specified: the compilerFilter must be in the whitelist.
+        // iorap compiler filters specified: the compilerFilter must be in the allowlist.
         if (mIorapCompilerFilters.indexOf(compilerFilter) != -1) {
             return true;
         }
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
index ee587f5..03352e3 100644
--- a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
+++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
@@ -112,10 +112,10 @@
             throw CompanionException("Association result can't be null.")
         }
 
-        val association = result.getParcelableExtra(
+        val association = checkNotNull(result.getParcelableExtra(
             CompanionDeviceManager.EXTRA_ASSOCIATION,
             AssociationInfo::class.java
-        )
+        ))
         val remoteDevice = association.associatedDevice?.getBluetoothDevice()!!
 
         // Register associated device
@@ -138,8 +138,8 @@
      */
     @Rpc(description = "Start permissions sync.")
     fun startPermissionsSync(associationId: Int) {
-        val pendingIntent = companionDeviceManager
-            .buildPermissionTransferUserConsentIntent(associationId)
+        val pendingIntent = checkNotNull(companionDeviceManager
+            .buildPermissionTransferUserConsentIntent(associationId))
         CompanionActivity.launchAndWait(context)
         CompanionActivity.startIntentSender(pendingIntent)
         confirmationUi.waitUntilSystemDataTransferConfirmationVisible()
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index e56ce81..63782f1 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -45,7 +45,7 @@
     private lateinit var mInputMonitor: InputMonitor
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        val inputManager = getSystemService(InputManager::class.java)
+        val inputManager = checkNotNull(getSystemService(InputManager::class.java))
         mInputMonitor = inputManager.monitorGestureInput(MONITOR_NAME, displayId)
         mInputEventReceiver = UnresponsiveReceiver(
                 mInputMonitor.getInputChannel(), Looper.myLooper()!!)
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
index 807f0c6..e60d8ef 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
@@ -23,6 +23,7 @@
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.requestFocusAndVerify;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
@@ -225,7 +226,7 @@
         Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity firstActivity = TestActivity.start(intent1);
         // Request view focus after app starts
-        mInstrumentation.runOnMainSync(firstActivity::requestFocus);
+        requestFocusAndVerify(firstActivity);
 
         Intent intent2 =
                 createIntent(
@@ -252,7 +253,7 @@
         Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent1);
         // Request view focus after app starts
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
 
         // Create second TestActivity
         Intent intent2 =
@@ -284,7 +285,7 @@
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request view focus after app starts
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
 
         // Find the editText and click it
         UiObject2 editTextUiObject =
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
index 320daee..2ac25f2 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
@@ -23,6 +23,7 @@
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.requestFocusAndVerify;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
@@ -96,7 +97,7 @@
         UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         eventually(
                 () ->
-                        assertWithMessage("Display rotation should be updated.")
+                        assertWithMessage("Display rotation should have been updated")
                                 .that(uiDevice.getDisplayRotation())
                                 .isEqualTo(mIsPortrait ? 0 : 1),
                 TIMEOUT);
@@ -104,7 +105,7 @@
         for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
             // TODO(b/291752364): Remove the explicit focus request once the issue with view focus
             //  change between fullscreen IME and actual editText is fixed.
-            callOnMainSync(editText::requestFocus);
+            requestFocusAndVerify(activity);
             verifyWindowAndViewFocus(editText, true, true);
             callOnMainSync(activity::showImeWithInputMethodManager);
             waitOnMainUntilImeIsShown(editText);
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
index 5c02124..5368025 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -29,6 +29,7 @@
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.isImeShown;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.requestFocusAndVerify;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
@@ -38,6 +39,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.os.Build;
@@ -96,7 +100,8 @@
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
+
         // Test only once if window flags set to save time.
         int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
         for (int i = 0; i < iterNum; i++) {
@@ -106,21 +111,19 @@
             verifyShowBehavior(activity);
 
             callOnMainSync(activity::hideImeWithInputMethodManager);
-
             verifyHideBehavior(activity);
         }
     }
 
     @Test
     public void testShowHideWithInputMethodManager_waitingAnimationEnd() {
+        assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
+
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
 
-        if (hasUnfocusableWindowFlags(activity)) {
-            return; // Skip to save time.
-        }
         activity.enableAnimationMonitoring();
         EditText editText = activity.getEditText();
         for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
@@ -128,12 +131,12 @@
             Log.i(TAG, msgPrefix + "start");
             callOnMainSync(activity::showImeWithInputMethodManager);
             waitOnMainUntil(
-                    msgPrefix + "IME should be visible",
+                    msgPrefix + "IME should have been shown",
                     () -> !activity.isAnimating() && isImeShown(editText));
 
             callOnMainSync(activity::hideImeWithInputMethodManager);
             waitOnMainUntil(
-                    msgPrefix + "IME should be hidden",
+                    msgPrefix + "IME should have been hidden",
                     () -> !activity.isAnimating() && !isImeShown(editText));
         }
     }
@@ -141,13 +144,13 @@
     @Test
     public void testShowHideWithInputMethodManager_intervalAfterHide() {
         // Regression test for b/221483132
+        assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
+
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
-        if (hasUnfocusableWindowFlags(activity)) {
-            return; // Skip to save time.
-        }
+        requestFocusAndVerify(activity);
+
         // Intervals = 10, 20, 30, ..., 100, 150, 200, ...
         List<Integer> intervals = new ArrayList<>();
         for (int i = 10; i < 100; i += 10) intervals.add(i);
@@ -165,14 +168,12 @@
 
     @Test
     public void testShowHideWithInputMethodManager_inSameFrame() {
+        assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
 
-        if (hasUnfocusableWindowFlags(activity)) {
-            return; // Skip to save time.
-        }
         // hidden -> show -> hide
         mInstrumentation.runOnMainSync(
                 () -> {
@@ -256,13 +257,12 @@
 
     @Test
     public void testShowHideWithWindowInsetsController_waitingVisibilityChange() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
+
         // Test only once if window flags set to save time.
         int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
         for (int i = 0; i < iterNum; i++) {
@@ -277,17 +277,13 @@
 
     @Test
     public void testShowHideWithWindowInsetsController_waitingAnimationEnd() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+        assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
 
-        if (hasUnfocusableWindowFlags(activity)) {
-            return; // Skip to save time.
-        }
         activity.enableAnimationMonitoring();
         EditText editText = activity.getEditText();
         for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
@@ -295,29 +291,25 @@
             Log.i(TAG, msgPrefix + "start");
             mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
             waitOnMainUntil(
-                    msgPrefix + "IME should be visible",
+                    msgPrefix + "IME should have been shown",
                     () -> !activity.isAnimating() && isImeShown(editText));
 
             mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
             waitOnMainUntil(
-                    msgPrefix + "IME should be hidden",
+                    msgPrefix + "IME should have been hidden",
                     () -> !activity.isAnimating() && !isImeShown(editText));
         }
     }
 
     @Test
     public void testShowHideWithWindowInsetsController_intervalAfterHide() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+        assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
 
-        if (hasUnfocusableWindowFlags(activity)) {
-            return; // Skip to save time.
-        }
         // Intervals = 10, 20, 30, ..., 100, 150, 200, ...
         List<Integer> intervals = new ArrayList<>();
         for (int i = 10; i < 100; i += 10) intervals.add(i);
@@ -335,17 +327,13 @@
 
     @Test
     public void testShowHideWithWindowInsetsController_inSameFrame() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+        assumeFalse("Has unfocusable window flags", hasUnfocusableWindowFlags(mWindowFocusFlags));
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         // Request focus after app starts to avoid triggering auto-show behavior.
-        mInstrumentation.runOnMainSync(activity::requestFocus);
+        requestFocusAndVerify(activity);
 
-        if (hasUnfocusableWindowFlags(activity)) {
-            return; // Skip to save time.
-        }
         // hidden -> show -> hide
         mInstrumentation.runOnMainSync(
                 () -> {
@@ -377,9 +365,7 @@
 
     @Test
     public void testShowWithWindowInsetsController_onCreate_requestFocus() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
         // Show with InputMethodManager at onCreate()
         Intent intent =
                 createIntent(
@@ -394,10 +380,8 @@
 
     @Test
     public void testShowWithWindowInsetsController_onCreate_notRequestFocus() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
-        // Show and hide with InputMethodManager at onCreate()
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+        // Show and hide with WindowInsetsController at onCreate()
         Intent intent =
                 createIntent(
                         mWindowFocusFlags,
@@ -411,10 +395,8 @@
 
     @Test
     public void testShowWithWindowInsetsController_afterStart_notRequestFocus() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
-        // Show and hide with InputMethodManager at onCreate()
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+        // Show and hide with WindowInsetsController at onCreate()
         Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
         TestActivity activity = TestActivity.start(intent);
         mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
@@ -425,7 +407,8 @@
 
     /**
      * Test IME hidden by calling show and hide IME consecutively with
-     * {@link android.view.WindowInsetsController} APIs in {@link android.app.Activity#onCreate}.
+     * {@link android.view.WindowInsetsController} APIs in
+     * {@link android.app.Activity#onCreate}.
      *
      * <p> Note for developers: Use {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNCHANGED}
      * window flag to avoid some softInputMode visibility flags may take presence over
@@ -436,13 +419,11 @@
      */
     @Test
     public void testHideWithWindowInsetsController_onCreate_requestFocus() {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
-            return;
-        }
+        assumeTrue("Is at least Android R", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
         if (mSoftInputFlags != SOFT_INPUT_STATE_UNCHANGED) {
             return;
         }
-        // Show and hide with InputMethodManager at onCreate()
+        // Show and hide with WindowInsetsController at onCreate()
         Intent intent =
                 createIntent(
                         mWindowFocusFlags,
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index 12556bc..c0c60ef 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -40,6 +40,7 @@
 import android.widget.EditText;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -149,6 +150,14 @@
     }
 
     /**
+     * Requests EditText view focus on the main thread, and assert this returns {@code true}.
+     */
+    public static void requestFocusAndVerify(TestActivity activity) {
+        boolean result = callOnMainSync(activity::requestFocus);
+        assertWithMessage("View focus request should have succeeded").that(result).isTrue();
+    }
+
+    /**
      * Waits until {@code pred} returns true, or throws on timeout.
      *
      * <p>The given {@code pred} will be called on the main thread.
@@ -161,7 +170,7 @@
     public static void waitOnMainUntilImeIsShown(View view) {
         eventually(
                 () ->
-                        assertWithMessage("IME should be shown")
+                        assertWithMessage("IME should have been shown")
                                 .that(callOnMainSync(() -> isImeShown(view)))
                                 .isTrue(),
                 TIMEOUT);
@@ -171,27 +180,28 @@
     public static void waitOnMainUntilImeIsHidden(View view) {
         eventually(
                 () ->
-                        assertWithMessage("IME should be hidden")
+                        assertWithMessage("IME should have been hidden")
                                 .that(callOnMainSync(() -> isImeShown(view)))
                                 .isFalse(),
                 TIMEOUT);
     }
 
-    /** Waits until window get focus, or throws on timeout. */
+    /** Waits until window gains focus, or throws on timeout. */
     public static void waitOnMainUntilWindowGainsFocus(View view) {
         eventually(
                 () ->
-                        assertWithMessage("Window should gain focus")
+                        assertWithMessage(
+                                "Window should have gained focus; value of hasWindowFocus:")
                                 .that(callOnMainSync(view::hasWindowFocus))
                                 .isTrue(),
                 TIMEOUT);
     }
 
-    /** Waits until view get focus, or throws on timeout. */
+    /** Waits until view gains focus, or throws on timeout. */
     public static void waitOnMainUntilViewGainsFocus(View view) {
         eventually(
                 () ->
-                        assertWithMessage("View should gain focus")
+                        assertWithMessage("View should have gained focus; value of hasFocus:")
                                 .that(callOnMainSync(view::hasFocus))
                                 .isTrue(),
                 TIMEOUT);
@@ -201,7 +211,7 @@
     public static void verifyImeIsAlwaysHidden(View view) {
         always(
                 () ->
-                        assertWithMessage("IME should be hidden")
+                        assertWithMessage("IME should have been hidden")
                                 .that(callOnMainSync(() -> isImeShown(view)))
                                 .isFalse(),
                 TIMEOUT);
@@ -211,7 +221,8 @@
     public static void verifyWindowNeverGainsFocus(View view) {
         always(
                 () ->
-                        assertWithMessage("window should never gain focus")
+                        assertWithMessage(
+                                "Window should not have gained focus; value of hasWindowFocus:")
                                 .that(callOnMainSync(view::hasWindowFocus))
                                 .isFalse(),
                 TIMEOUT);
@@ -221,7 +232,7 @@
     public static void verifyViewNeverGainsFocus(View view) {
         always(
                 () ->
-                        assertWithMessage("view should never gain ime focus")
+                        assertWithMessage("View should not have gained focus; value of hasFocus:")
                                 .that(callOnMainSync(view::hasFocus))
                                 .isFalse(),
                 TIMEOUT);
@@ -254,8 +265,23 @@
         }
     }
 
+    /**
+     * Returns {@code true} if the activity can't receive IME focus, based on its window flags,
+     * and {@code false} otherwise.
+     *
+     * @param activity the activity to check.
+     */
     public static boolean hasUnfocusableWindowFlags(Activity activity) {
-        int windowFlags = activity.getWindow().getAttributes().flags;
+        return hasUnfocusableWindowFlags(activity.getWindow().getAttributes().flags);
+    }
+
+    /**
+     * Returns {@code true} if the activity can't receive IME focus, based on its window flags,
+     * and {@code false} otherwise.
+     *
+     * @param windowFlags the window flags to check.
+     */
+    public static boolean hasUnfocusableWindowFlags(int windowFlags) {
         return (windowFlags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0
                 || (windowFlags & LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
                 || (windowFlags & LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
@@ -302,22 +328,26 @@
 
         private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
                 new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+                    @NonNull
                     @Override
                     public WindowInsetsAnimation.Bounds onStart(
-                            WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+                            @NonNull WindowInsetsAnimation animation,
+                            @NonNull WindowInsetsAnimation.Bounds bounds) {
                         mIsAnimating = true;
                         return super.onStart(animation, bounds);
                     }
 
                     @Override
-                    public void onEnd(WindowInsetsAnimation animation) {
+                    public void onEnd(@NonNull WindowInsetsAnimation animation) {
                         super.onEnd(animation);
                         mIsAnimating = false;
                     }
 
+                    @NonNull
                     @Override
                     public WindowInsets onProgress(
-                            WindowInsets insets, List<WindowInsetsAnimation> runningAnimations) {
+                            @NonNull WindowInsets insets,
+                            @NonNull List<WindowInsetsAnimation> runningAnimations) {
                         return insets;
                     }
                 };
@@ -394,7 +424,7 @@
                     getInputMethodManager()
                             .showSoftInput(mEditText, 0 /* flags */);
             if (showResult) {
-                Log.i(TAG, "IMM#showSoftInput successfully");
+                Log.i(TAG, "IMM#showSoftInput succeeded");
             } else {
                 Log.i(TAG, "IMM#showSoftInput failed");
             }
@@ -407,7 +437,7 @@
                     getInputMethodManager()
                             .hideSoftInputFromWindow(mEditText.getWindowToken(), 0 /* flags */);
             if (hideResult) {
-                Log.i(TAG, "IMM#hideSoftInput successfully");
+                Log.i(TAG, "IMM#hideSoftInput succeeded");
             } else {
                 Log.i(TAG, "IMM#hideSoftInput failed");
             }
@@ -421,7 +451,7 @@
             }
             Log.i(TAG, "showImeWithWIC()");
             WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
-            assertWithMessage("WindowInsetsController shouldn't be null.")
+            assertWithMessage("WindowInsetsController")
                     .that(windowInsetsController)
                     .isNotNull();
             windowInsetsController.show(WindowInsets.Type.ime());
@@ -434,7 +464,7 @@
             }
             Log.i(TAG, "hideImeWithWIC()");
             WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
-            assertWithMessage("WindowInsetsController shouldn't be null.")
+            assertWithMessage("WindowInsetsController")
                     .that(windowInsetsController)
                     .isNotNull();
             windowInsetsController.hide(WindowInsets.Type.ime());
@@ -482,13 +512,14 @@
             return mIsAnimating;
         }
 
-        public void requestFocus() {
-            boolean requestFocusResult = getEditText().requestFocus();
+        public boolean requestFocus() {
+            boolean requestFocusResult = mEditText.requestFocus();
             if (requestFocusResult) {
-                Log.i(TAG, "Request focus successfully");
+                Log.i(TAG, "View#requestFocus succeeded");
             } else {
-                Log.i(TAG, "Request focus failed");
+                Log.i(TAG, "View#requestFocus failed");
             }
+            return requestFocusResult;
         }
     }
 }
diff --git a/tests/Internal/src/stub/DummyWallpaperService.java b/tests/Internal/src/stub/DummyWallpaperService.java
index 084c036..db1b780 100644
--- a/tests/Internal/src/stub/DummyWallpaperService.java
+++ b/tests/Internal/src/stub/DummyWallpaperService.java
@@ -19,7 +19,7 @@
 import android.service.wallpaper.WallpaperService;
 
 /**
- * Dummy wallpaper service only for test purposes, won't draw anything.
+ * Placeholder wallpaper service only for test purposes, won't draw anything.
  */
 public class DummyWallpaperService extends WallpaperService {
     @Override
diff --git a/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
index 731be8e..a77950f 100644
--- a/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
+++ b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java
@@ -24,7 +24,7 @@
 import com.android.internal.compat.IPlatformCompat;
 
 /**
- * This is a dummy API to test gating
+ * This is a placeholder API to test gating
  *
  * @hide
  */
@@ -36,7 +36,7 @@
     public static final long CHANGE_SYSTEM_SERVER = 666016;
 
     /**
-     * Dummy method
+     * Placeholder method
      * @return "A" if change is enabled, "B" otherwise.
      */
     public static String dummyFunc() {
@@ -47,7 +47,7 @@
     }
 
     /**
-     * Dummy combined method
+     * Placeholder combined method
      * @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled,
                "1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled,
                "2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled,
@@ -68,7 +68,7 @@
     }
 
     /**
-     * Dummy api using system server API.
+     * Placeholder api using system server API.
      */
     public static boolean dummySystemServer(Context context) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
index c4bc600..43debb110 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
@@ -98,23 +98,23 @@
         (view.getParent() as ViewGroup).removeView(view)
         parent.addView(view)
 
-        view.findViewById<Button>(R.id.gainmap_metadata_done)!!.setOnClickListener {
+        view.requireViewById<Button>(R.id.gainmap_metadata_done).setOnClickListener {
             closeEditor()
         }
 
-        view.findViewById<Button>(R.id.gainmap_metadata_reset)!!.setOnClickListener {
+        view.requireViewById<Button>(R.id.gainmap_metadata_reset).setOnClickListener {
             resetGainmapMetadata()
         }
 
         updateMetadataUi()
 
-        val gainmapMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
-        val gainmapMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
-        val capacityMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
-        val capacityMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
-        val gammaSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
-        val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
-        val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+        val gainmapMinSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+        val gainmapMaxSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+        val capacityMinSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+        val capacityMaxSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+        val gammaSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+        val offsetSdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+        val offsetHdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
         arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
                 offsetSdrSeek, offsetHdrSeek).forEach {
             it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
@@ -139,13 +139,13 @@
     }
 
     private fun updateMetadataUi() {
-        val gainmapMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
-        val gainmapMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
-        val capacityMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
-        val capacityMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
-        val gammaSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
-        val offsetSdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
-        val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+        val gainmapMinSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+        val gainmapMaxSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+        val capacityMinSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+        val capacityMaxSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+        val gammaSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+        val offsetSdrSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+        val offsetHdrSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
 
         gainmapMinSeek.setProgress(
                 ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
@@ -165,19 +165,19 @@
                 ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
                         .toFloat() * maxProgress).toInt())
 
-        parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
                 "%.3f".format(currentMetadata.ratioMin))
-        parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
                 "%.3f".format(currentMetadata.ratioMax))
-        parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
                 "%.3f".format(currentMetadata.capacityMin))
-        parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
                 "%.3f".format(currentMetadata.capacityMax))
-        parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
                 "%.3f".format(currentMetadata.gamma))
-        parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
                 "%.5f".format(currentMetadata.offsetSdr))
-        parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
                 "%.5f".format(currentMetadata.offsetHdr))
     }
 
@@ -200,7 +200,7 @@
 
     private fun updateGainmapMin(normalized: Float) {
         val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
-        parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
                 "%.3f".format(newValue))
         currentMetadata.ratioMin = newValue
         gainmap.setRatioMin(newValue, newValue, newValue)
@@ -209,7 +209,7 @@
 
     private fun updateGainmapMax(normalized: Float) {
         val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
-        parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
                 "%.3f".format(newValue))
         currentMetadata.ratioMax = newValue
         gainmap.setRatioMax(newValue, newValue, newValue)
@@ -218,7 +218,7 @@
 
     private fun updateCapacityMin(normalized: Float) {
         val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
-        parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
                 "%.3f".format(newValue))
         currentMetadata.capacityMin = newValue
         gainmap.setMinDisplayRatioForHdrTransition(newValue)
@@ -227,7 +227,7 @@
 
     private fun updateCapacityMax(normalized: Float) {
         val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
-        parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
                 "%.3f".format(newValue))
         currentMetadata.capacityMax = newValue
         gainmap.setDisplayRatioForFullHdr(newValue)
@@ -236,7 +236,7 @@
 
     private fun updateGamma(normalized: Float) {
         val newValue = minGamma + normalized * (maxGamma - minGamma)
-        parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
                 "%.3f".format(newValue))
         currentMetadata.gamma = newValue
         gainmap.setGamma(newValue, newValue, newValue)
@@ -248,7 +248,7 @@
         if (normalized > 0.0f ) {
             newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
         }
-        parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
                 "%.5f".format(newValue))
         currentMetadata.offsetSdr = newValue
         gainmap.setEpsilonSdr(newValue, newValue, newValue)
@@ -260,7 +260,7 @@
         if (normalized > 0.0f ) {
             newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
         }
-        parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+        parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
                 "%.5f".format(newValue))
         currentMetadata.offsetHdr = newValue
         gainmap.setEpsilonHdr(newValue, newValue, newValue)
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 2f2578b..41baead 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -190,9 +190,9 @@
         sensorManager?.unregisterListener(sensorListener)
     }
 
-    override fun onDraw(canvas: Canvas?) {
+    override fun onDraw(canvas: Canvas) {
         updateGlassRenderNode()
-        canvas?.drawRenderNode(renderNode)
+        canvas.drawRenderNode(renderNode)
     }
 
     fun resetGyroOffsets() {
@@ -227,4 +227,4 @@
             renderNodeIsDirty = false
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 1400dde..c1a7bd9 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -30,7 +30,7 @@
  */
 class LockStateTrackingRule : TestRule {
     private val context: Context = getApplicationContext()
-    private val windowManager = WindowManagerGlobal.getWindowManagerService()
+    private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService())
 
     @Volatile lateinit var lockState: LockState
         private set
diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
index 4189baa..f1edca3 100644
--- a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
@@ -36,7 +36,7 @@
 class ScreenLockRule : TestRule {
     private val context: Context = getApplicationContext()
     private val uiDevice = UiDevice.getInstance(getInstrumentation())
-    private val windowManager = WindowManagerGlobal.getWindowManagerService()
+    private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService())
     private val lockPatternUtils = LockPatternUtils(context)
     private var instantLockSavedValue = false
 
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
new file mode 100644
index 0000000..7012daf
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.test;
+
+import com.android.server.LocalServices;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JUnit Rule helps override /restore {@link LocalServices} state.
+ */
+public class LocalServiceKeeperRule implements TestRule {
+
+    private final Map<Class<?>, Object> mOverriddenServices = new HashMap<>();
+    private final List<Class<?>> mAddedServices = new ArrayList<>();
+
+    private volatile boolean mRuleApplied = false;
+
+    /**
+     * Overrides service in LocalServices. Service will be restored to original after test run.
+     */
+    public <T> void overrideLocalService(Class<T> type, T service) {
+        if (!mRuleApplied) {
+            throw new IllegalStateException("Can't override service without applying rule");
+        }
+        if (mOverriddenServices.containsKey(type) || mAddedServices.contains(type)) {
+            throw new IllegalArgumentException("Type already overridden: " + type);
+        }
+
+        T currentService = LocalServices.getService(type);
+        if (currentService != null) {
+            mOverriddenServices.put(type, currentService);
+            LocalServices.removeServiceForTest(type);
+        } else {
+            mAddedServices.add(type);
+        }
+        LocalServices.addService(type, service);
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            public void evaluate() throws Throwable {
+                try {
+                    mRuleApplied = true;
+                    base.evaluate();
+                } finally {
+                    mRuleApplied = false;
+                    mAddedServices.forEach(LocalServices::removeServiceForTest);
+                    mOverriddenServices.forEach((clazz, service) -> {
+                        LocalServices.removeServiceForTest(clazz);
+                        LocalServices.addService((Class) clazz, service);
+                    });
+                    mAddedServices.clear();
+                    mOverriddenServices.clear();
+                }
+            }
+        };
+    }
+}
diff --git a/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
new file mode 100644
index 0000000..12f2fae
--- /dev/null
+++ b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+@SmallTest
+public class LocalServiceKeeperRuleTest {
+
+    private final Description mDescription = Description.createSuiteDescription("Description");
+
+    LocalServiceKeeperRule mRule = new LocalServiceKeeperRule();
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(TestService.class);
+    }
+
+    @Test
+    public void testFailedIfCalledOutsideOfTheRule() {
+        TestService service = new TestService() {};
+
+        assertThrows(IllegalStateException.class,
+                () -> mRule.overrideLocalService(TestService.class, service));
+    }
+
+    @Test
+    public void testSetsLocalServiceIfNotPresent() throws Throwable {
+        LocalServices.removeServiceForTest(TestService.class);
+        TestService service = new TestService() {};
+
+        runInRuleApply(() -> {
+            mRule.overrideLocalService(TestService.class, service);
+            assertEquals(service, LocalServices.getService(TestService.class));
+        });
+    }
+
+    @Test
+    public void testOverridesLocalServiceIfPresent() throws Throwable {
+        TestService service = new TestService() {};
+        LocalServices.addService(TestService.class, service);
+        TestService overriddenService = new TestService() {};
+
+        runInRuleApply(() -> {
+            mRule.overrideLocalService(TestService.class, overriddenService);
+            assertEquals(overriddenService, LocalServices.getService(TestService.class));
+        });
+    }
+
+    @Test
+    public void testDoesNotAllowToOverrideSameServiceTwice() throws Throwable {
+        TestService service = new TestService() {};
+
+        runInRuleApply(() -> {
+            mRule.overrideLocalService(TestService.class, service);
+            assertThrows(IllegalArgumentException.class,
+                    () -> mRule.overrideLocalService(TestService.class, service));
+        });
+    }
+
+    @Test
+    public void testRestroresLocalServiceAfterTestIfPresent() throws Throwable {
+        TestService expectedService = new TestService() {};
+        LocalServices.addService(TestService.class, expectedService);
+        TestService overriddenService = new TestService() {};
+
+        runInRuleApply(() -> mRule.overrideLocalService(TestService.class, overriddenService));
+
+        assertEquals(expectedService, LocalServices.getService(TestService.class));
+    }
+
+    @Test
+    public void testRemovesLocalServiceAfterTestIfNotPresent() throws Throwable {
+        LocalServices.removeServiceForTest(TestService.class);
+        TestService service = new TestService() {};
+
+        runInRuleApply(() -> mRule.overrideLocalService(TestService.class, service));
+
+        assertNull(LocalServices.getService(TestService.class));
+    }
+
+    private void runInRuleApply(Runnable runnable) throws Throwable {
+        Statement testStatement = new Statement() {
+            @Override
+            public void evaluate() {
+                runnable.run();
+            }
+        };
+        mRule.apply(testStatement, mDescription).evaluate();
+    }
+
+    interface TestService {
+    }
+}